Home | History | Annotate | Download | only in arm64
      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/instruction-selector-impl.h"
      6 #include "src/compiler/node-matchers.h"
      7 
      8 namespace v8 {
      9 namespace internal {
     10 namespace compiler {
     11 
     12 enum ImmediateMode {
     13   kArithmeticImm,  // 12 bit unsigned immediate shifted left 0 or 12 bits
     14   kShift32Imm,     // 0 - 31
     15   kShift64Imm,     // 0 - 63
     16   kLogical32Imm,
     17   kLogical64Imm,
     18   kLoadStoreImm8,   // signed 8 bit or 12 bit unsigned scaled by access size
     19   kLoadStoreImm16,
     20   kLoadStoreImm32,
     21   kLoadStoreImm64,
     22   kNoImmediate
     23 };
     24 
     25 
     26 // Adds Arm64-specific methods for generating operands.
     27 class Arm64OperandGenerator FINAL : public OperandGenerator {
     28  public:
     29   explicit Arm64OperandGenerator(InstructionSelector* selector)
     30       : OperandGenerator(selector) {}
     31 
     32   InstructionOperand* UseOperand(Node* node, ImmediateMode mode) {
     33     if (CanBeImmediate(node, mode)) {
     34       return UseImmediate(node);
     35     }
     36     return UseRegister(node);
     37   }
     38 
     39   bool CanBeImmediate(Node* node, ImmediateMode mode) {
     40     int64_t value;
     41     if (node->opcode() == IrOpcode::kInt32Constant)
     42       value = OpParameter<int32_t>(node);
     43     else if (node->opcode() == IrOpcode::kInt64Constant)
     44       value = OpParameter<int64_t>(node);
     45     else
     46       return false;
     47     unsigned ignored;
     48     switch (mode) {
     49       case kLogical32Imm:
     50         // TODO(dcarney): some unencodable values can be handled by
     51         // switching instructions.
     52         return Assembler::IsImmLogical(static_cast<uint64_t>(value), 32,
     53                                        &ignored, &ignored, &ignored);
     54       case kLogical64Imm:
     55         return Assembler::IsImmLogical(static_cast<uint64_t>(value), 64,
     56                                        &ignored, &ignored, &ignored);
     57       case kArithmeticImm:
     58         // TODO(dcarney): -values can be handled by instruction swapping
     59         return Assembler::IsImmAddSub(value);
     60       case kShift32Imm:
     61         return 0 <= value && value < 32;
     62       case kShift64Imm:
     63         return 0 <= value && value < 64;
     64       case kLoadStoreImm8:
     65         return IsLoadStoreImmediate(value, LSByte);
     66       case kLoadStoreImm16:
     67         return IsLoadStoreImmediate(value, LSHalfword);
     68       case kLoadStoreImm32:
     69         return IsLoadStoreImmediate(value, LSWord);
     70       case kLoadStoreImm64:
     71         return IsLoadStoreImmediate(value, LSDoubleWord);
     72       case kNoImmediate:
     73         return false;
     74     }
     75     return false;
     76   }
     77 
     78  private:
     79   bool IsLoadStoreImmediate(int64_t value, LSDataSize size) {
     80     return Assembler::IsImmLSScaled(value, size) ||
     81            Assembler::IsImmLSUnscaled(value);
     82   }
     83 };
     84 
     85 
     86 static void VisitRRR(InstructionSelector* selector, ArchOpcode opcode,
     87                      Node* node) {
     88   Arm64OperandGenerator g(selector);
     89   selector->Emit(opcode, g.DefineAsRegister(node),
     90                  g.UseRegister(node->InputAt(0)),
     91                  g.UseRegister(node->InputAt(1)));
     92 }
     93 
     94 
     95 static void VisitRRRFloat64(InstructionSelector* selector, ArchOpcode opcode,
     96                             Node* node) {
     97   Arm64OperandGenerator g(selector);
     98   selector->Emit(opcode, g.DefineAsRegister(node),
     99                  g.UseRegister(node->InputAt(0)),
    100                  g.UseRegister(node->InputAt(1)));
    101 }
    102 
    103 
    104 static void VisitRRO(InstructionSelector* selector, ArchOpcode opcode,
    105                      Node* node, ImmediateMode operand_mode) {
    106   Arm64OperandGenerator g(selector);
    107   selector->Emit(opcode, g.DefineAsRegister(node),
    108                  g.UseRegister(node->InputAt(0)),
    109                  g.UseOperand(node->InputAt(1), operand_mode));
    110 }
    111 
    112 
    113 // Shared routine for multiple binary operations.
    114 template <typename Matcher>
    115 static void VisitBinop(InstructionSelector* selector, Node* node,
    116                        InstructionCode opcode, ImmediateMode operand_mode,
    117                        FlagsContinuation* cont) {
    118   Arm64OperandGenerator g(selector);
    119   Matcher m(node);
    120   InstructionOperand* inputs[4];
    121   size_t input_count = 0;
    122   InstructionOperand* outputs[2];
    123   size_t output_count = 0;
    124 
    125   inputs[input_count++] = g.UseRegister(m.left().node());
    126   inputs[input_count++] = g.UseOperand(m.right().node(), operand_mode);
    127 
    128   if (cont->IsBranch()) {
    129     inputs[input_count++] = g.Label(cont->true_block());
    130     inputs[input_count++] = g.Label(cont->false_block());
    131   }
    132 
    133   outputs[output_count++] = g.DefineAsRegister(node);
    134   if (cont->IsSet()) {
    135     outputs[output_count++] = g.DefineAsRegister(cont->result());
    136   }
    137 
    138   DCHECK_NE(0, input_count);
    139   DCHECK_NE(0, output_count);
    140   DCHECK_GE(arraysize(inputs), input_count);
    141   DCHECK_GE(arraysize(outputs), output_count);
    142 
    143   Instruction* instr = selector->Emit(cont->Encode(opcode), output_count,
    144                                       outputs, input_count, inputs);
    145   if (cont->IsBranch()) instr->MarkAsControl();
    146 }
    147 
    148 
    149 // Shared routine for multiple binary operations.
    150 template <typename Matcher>
    151 static void VisitBinop(InstructionSelector* selector, Node* node,
    152                        ArchOpcode opcode, ImmediateMode operand_mode) {
    153   FlagsContinuation cont;
    154   VisitBinop<Matcher>(selector, node, opcode, operand_mode, &cont);
    155 }
    156 
    157 
    158 void InstructionSelector::VisitLoad(Node* node) {
    159   MachineType rep = RepresentationOf(OpParameter<LoadRepresentation>(node));
    160   MachineType typ = TypeOf(OpParameter<LoadRepresentation>(node));
    161   Arm64OperandGenerator g(this);
    162   Node* base = node->InputAt(0);
    163   Node* index = node->InputAt(1);
    164   ArchOpcode opcode;
    165   ImmediateMode immediate_mode = kNoImmediate;
    166   switch (rep) {
    167     case kRepFloat32:
    168       opcode = kArm64LdrS;
    169       immediate_mode = kLoadStoreImm32;
    170       break;
    171     case kRepFloat64:
    172       opcode = kArm64LdrD;
    173       immediate_mode = kLoadStoreImm64;
    174       break;
    175     case kRepBit:  // Fall through.
    176     case kRepWord8:
    177       opcode = typ == kTypeInt32 ? kArm64Ldrsb : kArm64Ldrb;
    178       immediate_mode = kLoadStoreImm8;
    179       break;
    180     case kRepWord16:
    181       opcode = typ == kTypeInt32 ? kArm64Ldrsh : kArm64Ldrh;
    182       immediate_mode = kLoadStoreImm16;
    183       break;
    184     case kRepWord32:
    185       opcode = kArm64LdrW;
    186       immediate_mode = kLoadStoreImm32;
    187       break;
    188     case kRepTagged:  // Fall through.
    189     case kRepWord64:
    190       opcode = kArm64Ldr;
    191       immediate_mode = kLoadStoreImm64;
    192       break;
    193     default:
    194       UNREACHABLE();
    195       return;
    196   }
    197   if (g.CanBeImmediate(index, immediate_mode)) {
    198     Emit(opcode | AddressingModeField::encode(kMode_MRI),
    199          g.DefineAsRegister(node), g.UseRegister(base), g.UseImmediate(index));
    200   } else {
    201     Emit(opcode | AddressingModeField::encode(kMode_MRR),
    202          g.DefineAsRegister(node), g.UseRegister(base), g.UseRegister(index));
    203   }
    204 }
    205 
    206 
    207 void InstructionSelector::VisitStore(Node* node) {
    208   Arm64OperandGenerator g(this);
    209   Node* base = node->InputAt(0);
    210   Node* index = node->InputAt(1);
    211   Node* value = node->InputAt(2);
    212 
    213   StoreRepresentation store_rep = OpParameter<StoreRepresentation>(node);
    214   MachineType rep = RepresentationOf(store_rep.machine_type());
    215   if (store_rep.write_barrier_kind() == kFullWriteBarrier) {
    216     DCHECK(rep == kRepTagged);
    217     // TODO(dcarney): refactor RecordWrite function to take temp registers
    218     //                and pass them here instead of using fixed regs
    219     // TODO(dcarney): handle immediate indices.
    220     InstructionOperand* temps[] = {g.TempRegister(x11), g.TempRegister(x12)};
    221     Emit(kArm64StoreWriteBarrier, NULL, g.UseFixed(base, x10),
    222          g.UseFixed(index, x11), g.UseFixed(value, x12), arraysize(temps),
    223          temps);
    224     return;
    225   }
    226   DCHECK_EQ(kNoWriteBarrier, store_rep.write_barrier_kind());
    227   ArchOpcode opcode;
    228   ImmediateMode immediate_mode = kNoImmediate;
    229   switch (rep) {
    230     case kRepFloat32:
    231       opcode = kArm64StrS;
    232       immediate_mode = kLoadStoreImm32;
    233       break;
    234     case kRepFloat64:
    235       opcode = kArm64StrD;
    236       immediate_mode = kLoadStoreImm64;
    237       break;
    238     case kRepBit:  // Fall through.
    239     case kRepWord8:
    240       opcode = kArm64Strb;
    241       immediate_mode = kLoadStoreImm8;
    242       break;
    243     case kRepWord16:
    244       opcode = kArm64Strh;
    245       immediate_mode = kLoadStoreImm16;
    246       break;
    247     case kRepWord32:
    248       opcode = kArm64StrW;
    249       immediate_mode = kLoadStoreImm32;
    250       break;
    251     case kRepTagged:  // Fall through.
    252     case kRepWord64:
    253       opcode = kArm64Str;
    254       immediate_mode = kLoadStoreImm64;
    255       break;
    256     default:
    257       UNREACHABLE();
    258       return;
    259   }
    260   if (g.CanBeImmediate(index, immediate_mode)) {
    261     Emit(opcode | AddressingModeField::encode(kMode_MRI), NULL,
    262          g.UseRegister(base), g.UseImmediate(index), g.UseRegister(value));
    263   } else {
    264     Emit(opcode | AddressingModeField::encode(kMode_MRR), NULL,
    265          g.UseRegister(base), g.UseRegister(index), g.UseRegister(value));
    266   }
    267 }
    268 
    269 
    270 void InstructionSelector::VisitWord32And(Node* node) {
    271   VisitBinop<Int32BinopMatcher>(this, node, kArm64And32, kLogical32Imm);
    272 }
    273 
    274 
    275 void InstructionSelector::VisitWord64And(Node* node) {
    276   VisitBinop<Int64BinopMatcher>(this, node, kArm64And, kLogical64Imm);
    277 }
    278 
    279 
    280 void InstructionSelector::VisitWord32Or(Node* node) {
    281   VisitBinop<Int32BinopMatcher>(this, node, kArm64Or32, kLogical32Imm);
    282 }
    283 
    284 
    285 void InstructionSelector::VisitWord64Or(Node* node) {
    286   VisitBinop<Int64BinopMatcher>(this, node, kArm64Or, kLogical64Imm);
    287 }
    288 
    289 
    290 void InstructionSelector::VisitWord32Xor(Node* node) {
    291   Arm64OperandGenerator g(this);
    292   Int32BinopMatcher m(node);
    293   if (m.right().Is(-1)) {
    294     Emit(kArm64Not32, g.DefineAsRegister(node), g.UseRegister(m.left().node()));
    295   } else {
    296     VisitBinop<Int32BinopMatcher>(this, node, kArm64Xor32, kLogical32Imm);
    297   }
    298 }
    299 
    300 
    301 void InstructionSelector::VisitWord64Xor(Node* node) {
    302   Arm64OperandGenerator g(this);
    303   Int64BinopMatcher m(node);
    304   if (m.right().Is(-1)) {
    305     Emit(kArm64Not, g.DefineAsRegister(node), g.UseRegister(m.left().node()));
    306   } else {
    307     VisitBinop<Int64BinopMatcher>(this, node, kArm64Xor, kLogical32Imm);
    308   }
    309 }
    310 
    311 
    312 void InstructionSelector::VisitWord32Shl(Node* node) {
    313   VisitRRO(this, kArm64Shl32, node, kShift32Imm);
    314 }
    315 
    316 
    317 void InstructionSelector::VisitWord64Shl(Node* node) {
    318   VisitRRO(this, kArm64Shl, node, kShift64Imm);
    319 }
    320 
    321 
    322 void InstructionSelector::VisitWord32Shr(Node* node) {
    323   VisitRRO(this, kArm64Shr32, node, kShift32Imm);
    324 }
    325 
    326 
    327 void InstructionSelector::VisitWord64Shr(Node* node) {
    328   VisitRRO(this, kArm64Shr, node, kShift64Imm);
    329 }
    330 
    331 
    332 void InstructionSelector::VisitWord32Sar(Node* node) {
    333   VisitRRO(this, kArm64Sar32, node, kShift32Imm);
    334 }
    335 
    336 
    337 void InstructionSelector::VisitWord64Sar(Node* node) {
    338   VisitRRO(this, kArm64Sar, node, kShift64Imm);
    339 }
    340 
    341 
    342 void InstructionSelector::VisitWord32Ror(Node* node) {
    343   VisitRRO(this, kArm64Ror32, node, kShift32Imm);
    344 }
    345 
    346 
    347 void InstructionSelector::VisitWord64Ror(Node* node) {
    348   VisitRRO(this, kArm64Ror, node, kShift64Imm);
    349 }
    350 
    351 
    352 void InstructionSelector::VisitInt32Add(Node* node) {
    353   VisitBinop<Int32BinopMatcher>(this, node, kArm64Add32, kArithmeticImm);
    354 }
    355 
    356 
    357 void InstructionSelector::VisitInt64Add(Node* node) {
    358   VisitBinop<Int64BinopMatcher>(this, node, kArm64Add, kArithmeticImm);
    359 }
    360 
    361 
    362 void InstructionSelector::VisitInt32Sub(Node* node) {
    363   Arm64OperandGenerator g(this);
    364   Int32BinopMatcher m(node);
    365   if (m.left().Is(0)) {
    366     Emit(kArm64Neg32, g.DefineAsRegister(node),
    367          g.UseRegister(m.right().node()));
    368   } else {
    369     VisitBinop<Int32BinopMatcher>(this, node, kArm64Sub32, kArithmeticImm);
    370   }
    371 }
    372 
    373 
    374 void InstructionSelector::VisitInt64Sub(Node* node) {
    375   Arm64OperandGenerator g(this);
    376   Int64BinopMatcher m(node);
    377   if (m.left().Is(0)) {
    378     Emit(kArm64Neg, g.DefineAsRegister(node), g.UseRegister(m.right().node()));
    379   } else {
    380     VisitBinop<Int64BinopMatcher>(this, node, kArm64Sub, kArithmeticImm);
    381   }
    382 }
    383 
    384 
    385 void InstructionSelector::VisitInt32Mul(Node* node) {
    386   VisitRRR(this, kArm64Mul32, node);
    387 }
    388 
    389 
    390 void InstructionSelector::VisitInt64Mul(Node* node) {
    391   VisitRRR(this, kArm64Mul, node);
    392 }
    393 
    394 
    395 void InstructionSelector::VisitInt32Div(Node* node) {
    396   VisitRRR(this, kArm64Idiv32, node);
    397 }
    398 
    399 
    400 void InstructionSelector::VisitInt64Div(Node* node) {
    401   VisitRRR(this, kArm64Idiv, node);
    402 }
    403 
    404 
    405 void InstructionSelector::VisitInt32UDiv(Node* node) {
    406   VisitRRR(this, kArm64Udiv32, node);
    407 }
    408 
    409 
    410 void InstructionSelector::VisitInt64UDiv(Node* node) {
    411   VisitRRR(this, kArm64Udiv, node);
    412 }
    413 
    414 
    415 void InstructionSelector::VisitInt32Mod(Node* node) {
    416   VisitRRR(this, kArm64Imod32, node);
    417 }
    418 
    419 
    420 void InstructionSelector::VisitInt64Mod(Node* node) {
    421   VisitRRR(this, kArm64Imod, node);
    422 }
    423 
    424 
    425 void InstructionSelector::VisitInt32UMod(Node* node) {
    426   VisitRRR(this, kArm64Umod32, node);
    427 }
    428 
    429 
    430 void InstructionSelector::VisitInt64UMod(Node* node) {
    431   VisitRRR(this, kArm64Umod, node);
    432 }
    433 
    434 
    435 void InstructionSelector::VisitChangeInt32ToFloat64(Node* node) {
    436   Arm64OperandGenerator g(this);
    437   Emit(kArm64Int32ToFloat64, g.DefineAsRegister(node),
    438        g.UseRegister(node->InputAt(0)));
    439 }
    440 
    441 
    442 void InstructionSelector::VisitChangeUint32ToFloat64(Node* node) {
    443   Arm64OperandGenerator g(this);
    444   Emit(kArm64Uint32ToFloat64, g.DefineAsRegister(node),
    445        g.UseRegister(node->InputAt(0)));
    446 }
    447 
    448 
    449 void InstructionSelector::VisitChangeFloat64ToInt32(Node* node) {
    450   Arm64OperandGenerator g(this);
    451   Emit(kArm64Float64ToInt32, g.DefineAsRegister(node),
    452        g.UseRegister(node->InputAt(0)));
    453 }
    454 
    455 
    456 void InstructionSelector::VisitChangeFloat64ToUint32(Node* node) {
    457   Arm64OperandGenerator g(this);
    458   Emit(kArm64Float64ToUint32, g.DefineAsRegister(node),
    459        g.UseRegister(node->InputAt(0)));
    460 }
    461 
    462 
    463 void InstructionSelector::VisitChangeInt32ToInt64(Node* node) {
    464   Arm64OperandGenerator g(this);
    465   Emit(kArm64Sxtw, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0)));
    466 }
    467 
    468 
    469 void InstructionSelector::VisitChangeUint32ToUint64(Node* node) {
    470   Arm64OperandGenerator g(this);
    471   Emit(kArm64Mov32, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0)));
    472 }
    473 
    474 
    475 void InstructionSelector::VisitTruncateInt64ToInt32(Node* node) {
    476   Arm64OperandGenerator g(this);
    477   Emit(kArm64Mov32, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0)));
    478 }
    479 
    480 
    481 void InstructionSelector::VisitFloat64Add(Node* node) {
    482   VisitRRRFloat64(this, kArm64Float64Add, node);
    483 }
    484 
    485 
    486 void InstructionSelector::VisitFloat64Sub(Node* node) {
    487   VisitRRRFloat64(this, kArm64Float64Sub, node);
    488 }
    489 
    490 
    491 void InstructionSelector::VisitFloat64Mul(Node* node) {
    492   VisitRRRFloat64(this, kArm64Float64Mul, node);
    493 }
    494 
    495 
    496 void InstructionSelector::VisitFloat64Div(Node* node) {
    497   VisitRRRFloat64(this, kArm64Float64Div, node);
    498 }
    499 
    500 
    501 void InstructionSelector::VisitFloat64Mod(Node* node) {
    502   Arm64OperandGenerator g(this);
    503   Emit(kArm64Float64Mod, g.DefineAsFixed(node, d0),
    504        g.UseFixed(node->InputAt(0), d0),
    505        g.UseFixed(node->InputAt(1), d1))->MarkAsCall();
    506 }
    507 
    508 
    509 void InstructionSelector::VisitFloat64Sqrt(Node* node) {
    510   Arm64OperandGenerator g(this);
    511   Emit(kArm64Float64Sqrt, g.DefineAsRegister(node),
    512        g.UseRegister(node->InputAt(0)));
    513 }
    514 
    515 
    516 void InstructionSelector::VisitInt32AddWithOverflow(Node* node,
    517                                                     FlagsContinuation* cont) {
    518   VisitBinop<Int32BinopMatcher>(this, node, kArm64Add32, kArithmeticImm, cont);
    519 }
    520 
    521 
    522 void InstructionSelector::VisitInt32SubWithOverflow(Node* node,
    523                                                     FlagsContinuation* cont) {
    524   VisitBinop<Int32BinopMatcher>(this, node, kArm64Sub32, kArithmeticImm, cont);
    525 }
    526 
    527 
    528 // Shared routine for multiple compare operations.
    529 static void VisitCompare(InstructionSelector* selector, InstructionCode opcode,
    530                          InstructionOperand* left, InstructionOperand* right,
    531                          FlagsContinuation* cont) {
    532   Arm64OperandGenerator g(selector);
    533   opcode = cont->Encode(opcode);
    534   if (cont->IsBranch()) {
    535     selector->Emit(opcode, NULL, left, right, g.Label(cont->true_block()),
    536                    g.Label(cont->false_block()))->MarkAsControl();
    537   } else {
    538     DCHECK(cont->IsSet());
    539     selector->Emit(opcode, g.DefineAsRegister(cont->result()), left, right);
    540   }
    541 }
    542 
    543 
    544 // Shared routine for multiple word compare operations.
    545 static void VisitWordCompare(InstructionSelector* selector, Node* node,
    546                              InstructionCode opcode, FlagsContinuation* cont,
    547                              bool commutative) {
    548   Arm64OperandGenerator g(selector);
    549   Node* left = node->InputAt(0);
    550   Node* right = node->InputAt(1);
    551 
    552   // Match immediates on left or right side of comparison.
    553   if (g.CanBeImmediate(right, kArithmeticImm)) {
    554     VisitCompare(selector, opcode, g.UseRegister(left), g.UseImmediate(right),
    555                  cont);
    556   } else if (g.CanBeImmediate(left, kArithmeticImm)) {
    557     if (!commutative) cont->Commute();
    558     VisitCompare(selector, opcode, g.UseRegister(right), g.UseImmediate(left),
    559                  cont);
    560   } else {
    561     VisitCompare(selector, opcode, g.UseRegister(left), g.UseRegister(right),
    562                  cont);
    563   }
    564 }
    565 
    566 
    567 void InstructionSelector::VisitWord32Test(Node* node, FlagsContinuation* cont) {
    568   switch (node->opcode()) {
    569     case IrOpcode::kInt32Add:
    570       return VisitWordCompare(this, node, kArm64Cmn32, cont, true);
    571     case IrOpcode::kInt32Sub:
    572       return VisitWordCompare(this, node, kArm64Cmp32, cont, false);
    573     case IrOpcode::kWord32And:
    574       return VisitWordCompare(this, node, kArm64Tst32, cont, true);
    575     default:
    576       break;
    577   }
    578 
    579   Arm64OperandGenerator g(this);
    580   VisitCompare(this, kArm64Tst32, g.UseRegister(node), g.UseRegister(node),
    581                cont);
    582 }
    583 
    584 
    585 void InstructionSelector::VisitWord64Test(Node* node, FlagsContinuation* cont) {
    586   switch (node->opcode()) {
    587     case IrOpcode::kWord64And:
    588       return VisitWordCompare(this, node, kArm64Tst, cont, true);
    589     default:
    590       break;
    591   }
    592 
    593   Arm64OperandGenerator g(this);
    594   VisitCompare(this, kArm64Tst, g.UseRegister(node), g.UseRegister(node), cont);
    595 }
    596 
    597 
    598 void InstructionSelector::VisitWord32Compare(Node* node,
    599                                              FlagsContinuation* cont) {
    600   VisitWordCompare(this, node, kArm64Cmp32, cont, false);
    601 }
    602 
    603 
    604 void InstructionSelector::VisitWord64Compare(Node* node,
    605                                              FlagsContinuation* cont) {
    606   VisitWordCompare(this, node, kArm64Cmp, cont, false);
    607 }
    608 
    609 
    610 void InstructionSelector::VisitFloat64Compare(Node* node,
    611                                               FlagsContinuation* cont) {
    612   Arm64OperandGenerator g(this);
    613   Node* left = node->InputAt(0);
    614   Node* right = node->InputAt(1);
    615   VisitCompare(this, kArm64Float64Cmp, g.UseRegister(left),
    616                g.UseRegister(right), cont);
    617 }
    618 
    619 
    620 void InstructionSelector::VisitCall(Node* call, BasicBlock* continuation,
    621                                     BasicBlock* deoptimization) {
    622   Arm64OperandGenerator g(this);
    623   CallDescriptor* descriptor = OpParameter<CallDescriptor*>(call);
    624 
    625   FrameStateDescriptor* frame_state_descriptor = NULL;
    626   if (descriptor->NeedsFrameState()) {
    627     frame_state_descriptor =
    628         GetFrameStateDescriptor(call->InputAt(descriptor->InputCount()));
    629   }
    630 
    631   CallBuffer buffer(zone(), descriptor, frame_state_descriptor);
    632 
    633   // Compute InstructionOperands for inputs and outputs.
    634   // TODO(turbofan): on ARM64 it's probably better to use the code object in a
    635   // register if there are multiple uses of it. Improve constant pool and the
    636   // heuristics in the register allocator for where to emit constants.
    637   InitializeCallBuffer(call, &buffer, true, false);
    638 
    639   // Push the arguments to the stack.
    640   bool pushed_count_uneven = buffer.pushed_nodes.size() & 1;
    641   int aligned_push_count = buffer.pushed_nodes.size();
    642   // TODO(dcarney): claim and poke probably take small immediates,
    643   //                loop here or whatever.
    644   // Bump the stack pointer(s).
    645   if (aligned_push_count > 0) {
    646     // TODO(dcarney): it would be better to bump the csp here only
    647     //                and emit paired stores with increment for non c frames.
    648     Emit(kArm64Claim | MiscField::encode(aligned_push_count), NULL);
    649   }
    650   // Move arguments to the stack.
    651   {
    652     int slot = buffer.pushed_nodes.size() - 1;
    653     // Emit the uneven pushes.
    654     if (pushed_count_uneven) {
    655       Node* input = buffer.pushed_nodes[slot];
    656       Emit(kArm64Poke | MiscField::encode(slot), NULL, g.UseRegister(input));
    657       slot--;
    658     }
    659     // Now all pushes can be done in pairs.
    660     for (; slot >= 0; slot -= 2) {
    661       Emit(kArm64PokePair | MiscField::encode(slot), NULL,
    662            g.UseRegister(buffer.pushed_nodes[slot]),
    663            g.UseRegister(buffer.pushed_nodes[slot - 1]));
    664     }
    665   }
    666 
    667   // Select the appropriate opcode based on the call type.
    668   InstructionCode opcode;
    669   switch (descriptor->kind()) {
    670     case CallDescriptor::kCallCodeObject: {
    671       opcode = kArchCallCodeObject;
    672       break;
    673     }
    674     case CallDescriptor::kCallJSFunction:
    675       opcode = kArchCallJSFunction;
    676       break;
    677     default:
    678       UNREACHABLE();
    679       return;
    680   }
    681   opcode |= MiscField::encode(descriptor->flags());
    682 
    683   // Emit the call instruction.
    684   Instruction* call_instr =
    685       Emit(opcode, buffer.outputs.size(), &buffer.outputs.front(),
    686            buffer.instruction_args.size(), &buffer.instruction_args.front());
    687 
    688   call_instr->MarkAsCall();
    689   if (deoptimization != NULL) {
    690     DCHECK(continuation != NULL);
    691     call_instr->MarkAsControl();
    692   }
    693 }
    694 
    695 }  // namespace compiler
    696 }  // namespace internal
    697 }  // namespace v8
    698