• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/base/bits.h"
6 #include "src/base/platform/wrappers.h"
7 #include "src/codegen/assembler-inl.h"
8 #include "src/codegen/machine-type.h"
9 #include "src/common/globals.h"
10 #include "src/compiler/backend/instruction-codes.h"
11 #include "src/compiler/backend/instruction-selector-impl.h"
12 #include "src/compiler/backend/instruction-selector.h"
13 #include "src/compiler/machine-operator.h"
14 #include "src/compiler/node-matchers.h"
15 #include "src/compiler/node-properties.h"
16 
17 namespace v8 {
18 namespace internal {
19 namespace compiler {
20 
21 enum ImmediateMode {
22   kArithmeticImm,  // 12 bit unsigned immediate shifted left 0 or 12 bits
23   kShift32Imm,     // 0 - 31
24   kShift64Imm,     // 0 - 63
25   kLogical32Imm,
26   kLogical64Imm,
27   kLoadStoreImm8,  // signed 8 bit or 12 bit unsigned scaled by access size
28   kLoadStoreImm16,
29   kLoadStoreImm32,
30   kLoadStoreImm64,
31   kNoImmediate
32 };
33 
34 // Adds Arm64-specific methods for generating operands.
35 class Arm64OperandGenerator final : public OperandGenerator {
36  public:
Arm64OperandGenerator(InstructionSelector * selector)37   explicit Arm64OperandGenerator(InstructionSelector* selector)
38       : OperandGenerator(selector) {}
39 
UseOperand(Node * node,ImmediateMode mode)40   InstructionOperand UseOperand(Node* node, ImmediateMode mode) {
41     if (CanBeImmediate(node, mode)) {
42       return UseImmediate(node);
43     }
44     return UseRegister(node);
45   }
46 
47   // Use the zero register if the node has the immediate value zero, otherwise
48   // assign a register.
UseRegisterOrImmediateZero(Node * node)49   InstructionOperand UseRegisterOrImmediateZero(Node* node) {
50     if ((IsIntegerConstant(node) && (GetIntegerConstantValue(node) == 0)) ||
51         (IsFloatConstant(node) &&
52          (bit_cast<int64_t>(GetFloatConstantValue(node)) == 0))) {
53       return UseImmediate(node);
54     }
55     return UseRegister(node);
56   }
57 
58   // Use the provided node if it has the required value, or create a
59   // TempImmediate otherwise.
UseImmediateOrTemp(Node * node,int32_t value)60   InstructionOperand UseImmediateOrTemp(Node* node, int32_t value) {
61     if (GetIntegerConstantValue(node) == value) {
62       return UseImmediate(node);
63     }
64     return TempImmediate(value);
65   }
66 
IsIntegerConstant(Node * node)67   bool IsIntegerConstant(Node* node) {
68     return (node->opcode() == IrOpcode::kInt32Constant) ||
69            (node->opcode() == IrOpcode::kInt64Constant);
70   }
71 
GetIntegerConstantValue(Node * node)72   int64_t GetIntegerConstantValue(Node* node) {
73     if (node->opcode() == IrOpcode::kInt32Constant) {
74       return OpParameter<int32_t>(node->op());
75     }
76     DCHECK_EQ(IrOpcode::kInt64Constant, node->opcode());
77     return OpParameter<int64_t>(node->op());
78   }
79 
IsFloatConstant(Node * node)80   bool IsFloatConstant(Node* node) {
81     return (node->opcode() == IrOpcode::kFloat32Constant) ||
82            (node->opcode() == IrOpcode::kFloat64Constant);
83   }
84 
GetFloatConstantValue(Node * node)85   double GetFloatConstantValue(Node* node) {
86     if (node->opcode() == IrOpcode::kFloat32Constant) {
87       return OpParameter<float>(node->op());
88     }
89     DCHECK_EQ(IrOpcode::kFloat64Constant, node->opcode());
90     return OpParameter<double>(node->op());
91   }
92 
CanBeImmediate(Node * node,ImmediateMode mode)93   bool CanBeImmediate(Node* node, ImmediateMode mode) {
94     return IsIntegerConstant(node) &&
95            CanBeImmediate(GetIntegerConstantValue(node), mode);
96   }
97 
CanBeImmediate(int64_t value,ImmediateMode mode)98   bool CanBeImmediate(int64_t value, ImmediateMode mode) {
99     unsigned ignored;
100     switch (mode) {
101       case kLogical32Imm:
102         // TODO(dcarney): some unencodable values can be handled by
103         // switching instructions.
104         return Assembler::IsImmLogical(static_cast<uint64_t>(value), 32,
105                                        &ignored, &ignored, &ignored);
106       case kLogical64Imm:
107         return Assembler::IsImmLogical(static_cast<uint64_t>(value), 64,
108                                        &ignored, &ignored, &ignored);
109       case kArithmeticImm:
110         return Assembler::IsImmAddSub(value);
111       case kLoadStoreImm8:
112         return IsLoadStoreImmediate(value, 0);
113       case kLoadStoreImm16:
114         return IsLoadStoreImmediate(value, 1);
115       case kLoadStoreImm32:
116         return IsLoadStoreImmediate(value, 2);
117       case kLoadStoreImm64:
118         return IsLoadStoreImmediate(value, 3);
119       case kNoImmediate:
120         return false;
121       case kShift32Imm:  // Fall through.
122       case kShift64Imm:
123         // Shift operations only observe the bottom 5 or 6 bits of the value.
124         // All possible shifts can be encoded by discarding bits which have no
125         // effect.
126         return true;
127     }
128     return false;
129   }
130 
CanBeLoadStoreShiftImmediate(Node * node,MachineRepresentation rep)131   bool CanBeLoadStoreShiftImmediate(Node* node, MachineRepresentation rep) {
132     // TODO(arm64): Load and Store on 128 bit Q registers is not supported yet.
133     DCHECK_GT(MachineRepresentation::kSimd128, rep);
134     return IsIntegerConstant(node) &&
135            (GetIntegerConstantValue(node) == ElementSizeLog2Of(rep));
136   }
137 
138  private:
IsLoadStoreImmediate(int64_t value,unsigned size)139   bool IsLoadStoreImmediate(int64_t value, unsigned size) {
140     return Assembler::IsImmLSScaled(value, size) ||
141            Assembler::IsImmLSUnscaled(value);
142   }
143 };
144 
145 namespace {
146 
VisitRR(InstructionSelector * selector,ArchOpcode opcode,Node * node)147 void VisitRR(InstructionSelector* selector, ArchOpcode opcode, Node* node) {
148   Arm64OperandGenerator g(selector);
149   selector->Emit(opcode, g.DefineAsRegister(node),
150                  g.UseRegister(node->InputAt(0)));
151 }
152 
VisitRR(InstructionSelector * selector,InstructionCode opcode,Node * node)153 void VisitRR(InstructionSelector* selector, InstructionCode opcode,
154              Node* node) {
155   Arm64OperandGenerator g(selector);
156   selector->Emit(opcode, g.DefineAsRegister(node),
157                  g.UseRegister(node->InputAt(0)));
158 }
159 
VisitRRR(InstructionSelector * selector,ArchOpcode opcode,Node * node)160 void VisitRRR(InstructionSelector* selector, ArchOpcode opcode, Node* node) {
161   Arm64OperandGenerator g(selector);
162   selector->Emit(opcode, g.DefineAsRegister(node),
163                  g.UseRegister(node->InputAt(0)),
164                  g.UseRegister(node->InputAt(1)));
165 }
166 
VisitRRR(InstructionSelector * selector,InstructionCode opcode,Node * node)167 void VisitRRR(InstructionSelector* selector, InstructionCode opcode,
168               Node* node) {
169   Arm64OperandGenerator g(selector);
170   selector->Emit(opcode, g.DefineAsRegister(node),
171                  g.UseRegister(node->InputAt(0)),
172                  g.UseRegister(node->InputAt(1)));
173 }
174 
VisitSimdShiftRRR(InstructionSelector * selector,ArchOpcode opcode,Node * node,int width)175 void VisitSimdShiftRRR(InstructionSelector* selector, ArchOpcode opcode,
176                        Node* node, int width) {
177   Arm64OperandGenerator g(selector);
178   if (g.IsIntegerConstant(node->InputAt(1))) {
179     if (g.GetIntegerConstantValue(node->InputAt(1)) % width == 0) {
180       selector->EmitIdentity(node);
181     } else {
182       selector->Emit(opcode, g.DefineAsRegister(node),
183                      g.UseRegister(node->InputAt(0)),
184                      g.UseImmediate(node->InputAt(1)));
185     }
186   } else {
187     selector->Emit(opcode, g.DefineAsRegister(node),
188                    g.UseRegister(node->InputAt(0)),
189                    g.UseRegister(node->InputAt(1)));
190   }
191 }
192 
VisitRRI(InstructionSelector * selector,InstructionCode opcode,Node * node)193 void VisitRRI(InstructionSelector* selector, InstructionCode opcode,
194               Node* node) {
195   Arm64OperandGenerator g(selector);
196   int32_t imm = OpParameter<int32_t>(node->op());
197   selector->Emit(opcode, g.DefineAsRegister(node),
198                  g.UseRegister(node->InputAt(0)), g.UseImmediate(imm));
199 }
200 
VisitRRO(InstructionSelector * selector,ArchOpcode opcode,Node * node,ImmediateMode operand_mode)201 void VisitRRO(InstructionSelector* selector, ArchOpcode opcode, Node* node,
202               ImmediateMode operand_mode) {
203   Arm64OperandGenerator g(selector);
204   selector->Emit(opcode, g.DefineAsRegister(node),
205                  g.UseRegister(node->InputAt(0)),
206                  g.UseOperand(node->InputAt(1), operand_mode));
207 }
208 
VisitRRIR(InstructionSelector * selector,InstructionCode opcode,Node * node)209 void VisitRRIR(InstructionSelector* selector, InstructionCode opcode,
210                Node* node) {
211   Arm64OperandGenerator g(selector);
212   int32_t imm = OpParameter<int32_t>(node->op());
213   selector->Emit(opcode, g.DefineAsRegister(node),
214                  g.UseRegister(node->InputAt(0)), g.UseImmediate(imm),
215                  g.UseUniqueRegister(node->InputAt(1)));
216 }
217 
218 struct ExtendingLoadMatcher {
ExtendingLoadMatcherv8::internal::compiler::__anon1f5f66280111::ExtendingLoadMatcher219   ExtendingLoadMatcher(Node* node, InstructionSelector* selector)
220       : matches_(false), selector_(selector), base_(nullptr), immediate_(0) {
221     Initialize(node);
222   }
223 
Matchesv8::internal::compiler::__anon1f5f66280111::ExtendingLoadMatcher224   bool Matches() const { return matches_; }
225 
basev8::internal::compiler::__anon1f5f66280111::ExtendingLoadMatcher226   Node* base() const {
227     DCHECK(Matches());
228     return base_;
229   }
immediatev8::internal::compiler::__anon1f5f66280111::ExtendingLoadMatcher230   int64_t immediate() const {
231     DCHECK(Matches());
232     return immediate_;
233   }
opcodev8::internal::compiler::__anon1f5f66280111::ExtendingLoadMatcher234   ArchOpcode opcode() const {
235     DCHECK(Matches());
236     return opcode_;
237   }
238 
239  private:
240   bool matches_;
241   InstructionSelector* selector_;
242   Node* base_;
243   int64_t immediate_;
244   ArchOpcode opcode_;
245 
Initializev8::internal::compiler::__anon1f5f66280111::ExtendingLoadMatcher246   void Initialize(Node* node) {
247     Int64BinopMatcher m(node);
248     // When loading a 64-bit value and shifting by 32, we should
249     // just load and sign-extend the interesting 4 bytes instead.
250     // This happens, for example, when we're loading and untagging SMIs.
251     DCHECK(m.IsWord64Sar());
252     if (m.left().IsLoad() && m.right().Is(32) &&
253         selector_->CanCover(m.node(), m.left().node())) {
254       Arm64OperandGenerator g(selector_);
255       Node* load = m.left().node();
256       Node* offset = load->InputAt(1);
257       base_ = load->InputAt(0);
258       opcode_ = kArm64Ldrsw;
259       if (g.IsIntegerConstant(offset)) {
260         immediate_ = g.GetIntegerConstantValue(offset) + 4;
261         matches_ = g.CanBeImmediate(immediate_, kLoadStoreImm32);
262       }
263     }
264   }
265 };
266 
TryMatchExtendingLoad(InstructionSelector * selector,Node * node)267 bool TryMatchExtendingLoad(InstructionSelector* selector, Node* node) {
268   ExtendingLoadMatcher m(node, selector);
269   return m.Matches();
270 }
271 
TryEmitExtendingLoad(InstructionSelector * selector,Node * node)272 bool TryEmitExtendingLoad(InstructionSelector* selector, Node* node) {
273   ExtendingLoadMatcher m(node, selector);
274   Arm64OperandGenerator g(selector);
275   if (m.Matches()) {
276     InstructionOperand inputs[2];
277     inputs[0] = g.UseRegister(m.base());
278     InstructionCode opcode =
279         m.opcode() | AddressingModeField::encode(kMode_MRI);
280     DCHECK(is_int32(m.immediate()));
281     inputs[1] = g.TempImmediate(static_cast<int32_t>(m.immediate()));
282     InstructionOperand outputs[] = {g.DefineAsRegister(node)};
283     selector->Emit(opcode, arraysize(outputs), outputs, arraysize(inputs),
284                    inputs);
285     return true;
286   }
287   return false;
288 }
289 
TryMatchAnyShift(InstructionSelector * selector,Node * node,Node * input_node,InstructionCode * opcode,bool try_ror)290 bool TryMatchAnyShift(InstructionSelector* selector, Node* node,
291                       Node* input_node, InstructionCode* opcode, bool try_ror) {
292   Arm64OperandGenerator g(selector);
293 
294   if (!selector->CanCover(node, input_node)) return false;
295   if (input_node->InputCount() != 2) return false;
296   if (!g.IsIntegerConstant(input_node->InputAt(1))) return false;
297 
298   switch (input_node->opcode()) {
299     case IrOpcode::kWord32Shl:
300     case IrOpcode::kWord64Shl:
301       *opcode |= AddressingModeField::encode(kMode_Operand2_R_LSL_I);
302       return true;
303     case IrOpcode::kWord32Shr:
304     case IrOpcode::kWord64Shr:
305       *opcode |= AddressingModeField::encode(kMode_Operand2_R_LSR_I);
306       return true;
307     case IrOpcode::kWord32Sar:
308       *opcode |= AddressingModeField::encode(kMode_Operand2_R_ASR_I);
309       return true;
310     case IrOpcode::kWord64Sar:
311       if (TryMatchExtendingLoad(selector, input_node)) return false;
312       *opcode |= AddressingModeField::encode(kMode_Operand2_R_ASR_I);
313       return true;
314     case IrOpcode::kWord32Ror:
315     case IrOpcode::kWord64Ror:
316       if (try_ror) {
317         *opcode |= AddressingModeField::encode(kMode_Operand2_R_ROR_I);
318         return true;
319       }
320       return false;
321     default:
322       return false;
323   }
324 }
325 
TryMatchAnyExtend(Arm64OperandGenerator * g,InstructionSelector * selector,Node * node,Node * left_node,Node * right_node,InstructionOperand * left_op,InstructionOperand * right_op,InstructionCode * opcode)326 bool TryMatchAnyExtend(Arm64OperandGenerator* g, InstructionSelector* selector,
327                        Node* node, Node* left_node, Node* right_node,
328                        InstructionOperand* left_op,
329                        InstructionOperand* right_op, InstructionCode* opcode) {
330   if (!selector->CanCover(node, right_node)) return false;
331 
332   NodeMatcher nm(right_node);
333 
334   if (nm.IsWord32And()) {
335     Int32BinopMatcher mright(right_node);
336     if (mright.right().Is(0xFF) || mright.right().Is(0xFFFF)) {
337       int32_t mask = mright.right().ResolvedValue();
338       *left_op = g->UseRegister(left_node);
339       *right_op = g->UseRegister(mright.left().node());
340       *opcode |= AddressingModeField::encode(
341           (mask == 0xFF) ? kMode_Operand2_R_UXTB : kMode_Operand2_R_UXTH);
342       return true;
343     }
344   } else if (nm.IsWord32Sar()) {
345     Int32BinopMatcher mright(right_node);
346     if (selector->CanCover(mright.node(), mright.left().node()) &&
347         mright.left().IsWord32Shl()) {
348       Int32BinopMatcher mleft_of_right(mright.left().node());
349       if ((mright.right().Is(16) && mleft_of_right.right().Is(16)) ||
350           (mright.right().Is(24) && mleft_of_right.right().Is(24))) {
351         int32_t shift = mright.right().ResolvedValue();
352         *left_op = g->UseRegister(left_node);
353         *right_op = g->UseRegister(mleft_of_right.left().node());
354         *opcode |= AddressingModeField::encode(
355             (shift == 24) ? kMode_Operand2_R_SXTB : kMode_Operand2_R_SXTH);
356         return true;
357       }
358     }
359   } else if (nm.IsChangeInt32ToInt64()) {
360     // Use extended register form.
361     *opcode |= AddressingModeField::encode(kMode_Operand2_R_SXTW);
362     *left_op = g->UseRegister(left_node);
363     *right_op = g->UseRegister(right_node->InputAt(0));
364     return true;
365   }
366   return false;
367 }
368 
TryMatchLoadStoreShift(Arm64OperandGenerator * g,InstructionSelector * selector,MachineRepresentation rep,Node * node,Node * index,InstructionOperand * index_op,InstructionOperand * shift_immediate_op)369 bool TryMatchLoadStoreShift(Arm64OperandGenerator* g,
370                             InstructionSelector* selector,
371                             MachineRepresentation rep, Node* node, Node* index,
372                             InstructionOperand* index_op,
373                             InstructionOperand* shift_immediate_op) {
374   if (!selector->CanCover(node, index)) return false;
375   if (index->InputCount() != 2) return false;
376   Node* left = index->InputAt(0);
377   Node* right = index->InputAt(1);
378   switch (index->opcode()) {
379     case IrOpcode::kWord32Shl:
380     case IrOpcode::kWord64Shl:
381       if (!g->CanBeLoadStoreShiftImmediate(right, rep)) {
382         return false;
383       }
384       *index_op = g->UseRegister(left);
385       *shift_immediate_op = g->UseImmediate(right);
386       return true;
387     default:
388       return false;
389   }
390 }
391 
392 // Bitfields describing binary operator properties:
393 // CanCommuteField is true if we can switch the two operands, potentially
394 // requiring commuting the flags continuation condition.
395 using CanCommuteField = base::BitField8<bool, 1, 1>;
396 // MustCommuteCondField is true when we need to commute the flags continuation
397 // condition in order to switch the operands.
398 using MustCommuteCondField = base::BitField8<bool, 2, 1>;
399 // IsComparisonField is true when the operation is a comparison and has no other
400 // result other than the condition.
401 using IsComparisonField = base::BitField8<bool, 3, 1>;
402 // IsAddSubField is true when an instruction is encoded as ADD or SUB.
403 using IsAddSubField = base::BitField8<bool, 4, 1>;
404 
405 // Get properties of a binary operator.
GetBinopProperties(InstructionCode opcode)406 uint8_t GetBinopProperties(InstructionCode opcode) {
407   uint8_t result = 0;
408   switch (opcode) {
409     case kArm64Cmp32:
410     case kArm64Cmp:
411       // We can commute CMP by switching the inputs and commuting
412       // the flags continuation.
413       result = CanCommuteField::update(result, true);
414       result = MustCommuteCondField::update(result, true);
415       result = IsComparisonField::update(result, true);
416       // The CMP and CMN instructions are encoded as SUB or ADD
417       // with zero output register, and therefore support the same
418       // operand modes.
419       result = IsAddSubField::update(result, true);
420       break;
421     case kArm64Cmn32:
422     case kArm64Cmn:
423       result = CanCommuteField::update(result, true);
424       result = IsComparisonField::update(result, true);
425       result = IsAddSubField::update(result, true);
426       break;
427     case kArm64Add32:
428     case kArm64Add:
429       result = CanCommuteField::update(result, true);
430       result = IsAddSubField::update(result, true);
431       break;
432     case kArm64Sub32:
433     case kArm64Sub:
434       result = IsAddSubField::update(result, true);
435       break;
436     case kArm64Tst32:
437     case kArm64Tst:
438       result = CanCommuteField::update(result, true);
439       result = IsComparisonField::update(result, true);
440       break;
441     case kArm64And32:
442     case kArm64And:
443     case kArm64Or32:
444     case kArm64Or:
445     case kArm64Eor32:
446     case kArm64Eor:
447       result = CanCommuteField::update(result, true);
448       break;
449     default:
450       UNREACHABLE();
451   }
452   DCHECK_IMPLIES(MustCommuteCondField::decode(result),
453                  CanCommuteField::decode(result));
454   return result;
455 }
456 
457 // Shared routine for multiple binary operations.
458 template <typename Matcher>
VisitBinop(InstructionSelector * selector,Node * node,InstructionCode opcode,ImmediateMode operand_mode,FlagsContinuation * cont)459 void VisitBinop(InstructionSelector* selector, Node* node,
460                 InstructionCode opcode, ImmediateMode operand_mode,
461                 FlagsContinuation* cont) {
462   Arm64OperandGenerator g(selector);
463   InstructionOperand inputs[5];
464   size_t input_count = 0;
465   InstructionOperand outputs[1];
466   size_t output_count = 0;
467 
468   Node* left_node = node->InputAt(0);
469   Node* right_node = node->InputAt(1);
470 
471   uint8_t properties = GetBinopProperties(opcode);
472   bool can_commute = CanCommuteField::decode(properties);
473   bool must_commute_cond = MustCommuteCondField::decode(properties);
474   bool is_add_sub = IsAddSubField::decode(properties);
475 
476   if (g.CanBeImmediate(right_node, operand_mode)) {
477     inputs[input_count++] = g.UseRegister(left_node);
478     inputs[input_count++] = g.UseImmediate(right_node);
479   } else if (can_commute && g.CanBeImmediate(left_node, operand_mode)) {
480     if (must_commute_cond) cont->Commute();
481     inputs[input_count++] = g.UseRegister(right_node);
482     inputs[input_count++] = g.UseImmediate(left_node);
483   } else if (is_add_sub &&
484              TryMatchAnyExtend(&g, selector, node, left_node, right_node,
485                                &inputs[0], &inputs[1], &opcode)) {
486     input_count += 2;
487   } else if (is_add_sub && can_commute &&
488              TryMatchAnyExtend(&g, selector, node, right_node, left_node,
489                                &inputs[0], &inputs[1], &opcode)) {
490     if (must_commute_cond) cont->Commute();
491     input_count += 2;
492   } else if (TryMatchAnyShift(selector, node, right_node, &opcode,
493                               !is_add_sub)) {
494     Matcher m_shift(right_node);
495     inputs[input_count++] = g.UseRegisterOrImmediateZero(left_node);
496     inputs[input_count++] = g.UseRegister(m_shift.left().node());
497     // We only need at most the last 6 bits of the shift.
498     inputs[input_count++] = g.UseImmediate(
499         static_cast<int>(m_shift.right().ResolvedValue() & 0x3F));
500   } else if (can_commute && TryMatchAnyShift(selector, node, left_node, &opcode,
501                                              !is_add_sub)) {
502     if (must_commute_cond) cont->Commute();
503     Matcher m_shift(left_node);
504     inputs[input_count++] = g.UseRegisterOrImmediateZero(right_node);
505     inputs[input_count++] = g.UseRegister(m_shift.left().node());
506     // We only need at most the last 6 bits of the shift.
507     inputs[input_count++] = g.UseImmediate(
508         static_cast<int>(m_shift.right().ResolvedValue() & 0x3F));
509   } else {
510     inputs[input_count++] = g.UseRegisterOrImmediateZero(left_node);
511     inputs[input_count++] = g.UseRegister(right_node);
512   }
513 
514   if (!IsComparisonField::decode(properties)) {
515     outputs[output_count++] = g.DefineAsRegister(node);
516   }
517 
518   if (cont->IsSelect()) {
519     inputs[input_count++] = g.UseRegister(cont->true_value());
520     inputs[input_count++] = g.UseRegister(cont->false_value());
521   }
522 
523   DCHECK_NE(0u, input_count);
524   DCHECK((output_count != 0) || IsComparisonField::decode(properties));
525   DCHECK_GE(arraysize(inputs), input_count);
526   DCHECK_GE(arraysize(outputs), output_count);
527 
528   selector->EmitWithContinuation(opcode, output_count, outputs, input_count,
529                                  inputs, cont);
530 }
531 
532 // Shared routine for multiple binary operations.
533 template <typename Matcher>
VisitBinop(InstructionSelector * selector,Node * node,ArchOpcode opcode,ImmediateMode operand_mode)534 void VisitBinop(InstructionSelector* selector, Node* node, ArchOpcode opcode,
535                 ImmediateMode operand_mode) {
536   FlagsContinuation cont;
537   VisitBinop<Matcher>(selector, node, opcode, operand_mode, &cont);
538 }
539 
540 template <typename Matcher>
VisitAddSub(InstructionSelector * selector,Node * node,ArchOpcode opcode,ArchOpcode negate_opcode)541 void VisitAddSub(InstructionSelector* selector, Node* node, ArchOpcode opcode,
542                  ArchOpcode negate_opcode) {
543   Arm64OperandGenerator g(selector);
544   Matcher m(node);
545   if (m.right().HasResolvedValue() && (m.right().ResolvedValue() < 0) &&
546       (m.right().ResolvedValue() > std::numeric_limits<int>::min()) &&
547       g.CanBeImmediate(-m.right().ResolvedValue(), kArithmeticImm)) {
548     selector->Emit(
549         negate_opcode, g.DefineAsRegister(node), g.UseRegister(m.left().node()),
550         g.TempImmediate(static_cast<int32_t>(-m.right().ResolvedValue())));
551   } else {
552     VisitBinop<Matcher>(selector, node, opcode, kArithmeticImm);
553   }
554 }
555 
556 // For multiplications by immediate of the form x * (2^k + 1), where k > 0,
557 // return the value of k, otherwise return zero. This is used to reduce the
558 // multiplication to addition with left shift: x + (x << k).
559 template <typename Matcher>
LeftShiftForReducedMultiply(Matcher * m)560 int32_t LeftShiftForReducedMultiply(Matcher* m) {
561   DCHECK(m->IsInt32Mul() || m->IsInt64Mul());
562   if (m->right().HasResolvedValue() && m->right().ResolvedValue() >= 3) {
563     uint64_t value_minus_one = m->right().ResolvedValue() - 1;
564     if (base::bits::IsPowerOfTwo(value_minus_one)) {
565       return base::bits::WhichPowerOfTwo(value_minus_one);
566     }
567   }
568   return 0;
569 }
570 
571 }  // namespace
572 
VisitStackSlot(Node * node)573 void InstructionSelector::VisitStackSlot(Node* node) {
574   StackSlotRepresentation rep = StackSlotRepresentationOf(node->op());
575   int slot = frame_->AllocateSpillSlot(rep.size(), rep.alignment());
576   OperandGenerator g(this);
577 
578   Emit(kArchStackSlot, g.DefineAsRegister(node),
579        sequence()->AddImmediate(Constant(slot)), 0, nullptr);
580 }
581 
VisitAbortCSADcheck(Node * node)582 void InstructionSelector::VisitAbortCSADcheck(Node* node) {
583   Arm64OperandGenerator g(this);
584   Emit(kArchAbortCSADcheck, g.NoOutput(), g.UseFixed(node->InputAt(0), x1));
585 }
586 
EmitLoad(InstructionSelector * selector,Node * node,InstructionCode opcode,ImmediateMode immediate_mode,MachineRepresentation rep,Node * output=nullptr)587 void EmitLoad(InstructionSelector* selector, Node* node, InstructionCode opcode,
588               ImmediateMode immediate_mode, MachineRepresentation rep,
589               Node* output = nullptr) {
590   Arm64OperandGenerator g(selector);
591   Node* base = node->InputAt(0);
592   Node* index = node->InputAt(1);
593   InstructionOperand inputs[3];
594   size_t input_count = 0;
595   InstructionOperand outputs[1];
596 
597   // If output is not nullptr, use that as the output register. This
598   // is used when we merge a conversion into the load.
599   outputs[0] = g.DefineAsRegister(output == nullptr ? node : output);
600 
601   ExternalReferenceMatcher m(base);
602   if (m.HasResolvedValue() && g.IsIntegerConstant(index) &&
603       selector->CanAddressRelativeToRootsRegister(m.ResolvedValue())) {
604     ptrdiff_t const delta =
605         g.GetIntegerConstantValue(index) +
606         TurboAssemblerBase::RootRegisterOffsetForExternalReference(
607             selector->isolate(), m.ResolvedValue());
608     input_count = 1;
609     // Check that the delta is a 32-bit integer due to the limitations of
610     // immediate operands.
611     if (is_int32(delta)) {
612       inputs[0] = g.UseImmediate(static_cast<int32_t>(delta));
613       opcode |= AddressingModeField::encode(kMode_Root);
614       selector->Emit(opcode, arraysize(outputs), outputs, input_count, inputs);
615       return;
616     }
617   }
618 
619   inputs[0] = g.UseRegister(base);
620 
621   if (g.CanBeImmediate(index, immediate_mode)) {
622     input_count = 2;
623     inputs[1] = g.UseImmediate(index);
624     opcode |= AddressingModeField::encode(kMode_MRI);
625   } else if (TryMatchLoadStoreShift(&g, selector, rep, node, index, &inputs[1],
626                                     &inputs[2])) {
627     input_count = 3;
628     opcode |= AddressingModeField::encode(kMode_Operand2_R_LSL_I);
629   } else {
630     input_count = 2;
631     inputs[1] = g.UseRegister(index);
632     opcode |= AddressingModeField::encode(kMode_MRR);
633   }
634 
635   selector->Emit(opcode, arraysize(outputs), outputs, input_count, inputs);
636 }
637 
638 namespace {
639 // Manually add base and index into a register to get the actual address.
640 // This should be used prior to instructions that only support
641 // immediate/post-index addressing, like ld1 and st1.
EmitAddBeforeLoadOrStore(InstructionSelector * selector,Node * node,InstructionCode * opcode)642 InstructionOperand EmitAddBeforeLoadOrStore(InstructionSelector* selector,
643                                             Node* node,
644                                             InstructionCode* opcode) {
645   Arm64OperandGenerator g(selector);
646   InstructionOperand addr = g.TempRegister();
647   selector->Emit(kArm64Add, addr, g.UseRegister(node->InputAt(0)),
648                  g.UseRegister(node->InputAt(1)));
649   *opcode |= AddressingModeField::encode(kMode_MRI);
650   return addr;
651 }
652 }  // namespace
653 
VisitLoadLane(Node * node)654 void InstructionSelector::VisitLoadLane(Node* node) {
655   LoadLaneParameters params = LoadLaneParametersOf(node->op());
656   DCHECK(
657       params.rep == MachineType::Int8() || params.rep == MachineType::Int16() ||
658       params.rep == MachineType::Int32() || params.rep == MachineType::Int64());
659 
660   InstructionCode opcode = kArm64LoadLane;
661   opcode |= LaneSizeField::encode(params.rep.MemSize() * kBitsPerByte);
662   if (params.kind == MemoryAccessKind::kProtected) {
663     opcode |= AccessModeField::encode(kMemoryAccessProtected);
664   }
665 
666   Arm64OperandGenerator g(this);
667   InstructionOperand addr = EmitAddBeforeLoadOrStore(this, node, &opcode);
668   Emit(opcode, g.DefineSameAsFirst(node), g.UseRegister(node->InputAt(2)),
669        g.UseImmediate(params.laneidx), addr, g.TempImmediate(0));
670 }
671 
VisitStoreLane(Node * node)672 void InstructionSelector::VisitStoreLane(Node* node) {
673   StoreLaneParameters params = StoreLaneParametersOf(node->op());
674   DCHECK_LE(MachineRepresentation::kWord8, params.rep);
675   DCHECK_GE(MachineRepresentation::kWord64, params.rep);
676 
677   InstructionCode opcode = kArm64StoreLane;
678   opcode |=
679       LaneSizeField::encode(ElementSizeInBytes(params.rep) * kBitsPerByte);
680   if (params.kind == MemoryAccessKind::kProtected) {
681     opcode |= AccessModeField::encode(kMemoryAccessProtected);
682   }
683 
684   Arm64OperandGenerator g(this);
685   InstructionOperand addr = EmitAddBeforeLoadOrStore(this, node, &opcode);
686   InstructionOperand inputs[4] = {
687       g.UseRegister(node->InputAt(2)),
688       g.UseImmediate(params.laneidx),
689       addr,
690       g.TempImmediate(0),
691   };
692 
693   Emit(opcode, 0, nullptr, 4, inputs);
694 }
695 
VisitLoadTransform(Node * node)696 void InstructionSelector::VisitLoadTransform(Node* node) {
697   LoadTransformParameters params = LoadTransformParametersOf(node->op());
698   InstructionCode opcode = kArchNop;
699   bool require_add = false;
700   switch (params.transformation) {
701     case LoadTransformation::kS128Load8Splat:
702       opcode = kArm64LoadSplat;
703       opcode |= LaneSizeField::encode(8);
704       require_add = true;
705       break;
706     case LoadTransformation::kS128Load16Splat:
707       opcode = kArm64LoadSplat;
708       opcode |= LaneSizeField::encode(16);
709       require_add = true;
710       break;
711     case LoadTransformation::kS128Load32Splat:
712       opcode = kArm64LoadSplat;
713       opcode |= LaneSizeField::encode(32);
714       require_add = true;
715       break;
716     case LoadTransformation::kS128Load64Splat:
717       opcode = kArm64LoadSplat;
718       opcode |= LaneSizeField::encode(64);
719       require_add = true;
720       break;
721     case LoadTransformation::kS128Load8x8S:
722       opcode = kArm64S128Load8x8S;
723       break;
724     case LoadTransformation::kS128Load8x8U:
725       opcode = kArm64S128Load8x8U;
726       break;
727     case LoadTransformation::kS128Load16x4S:
728       opcode = kArm64S128Load16x4S;
729       break;
730     case LoadTransformation::kS128Load16x4U:
731       opcode = kArm64S128Load16x4U;
732       break;
733     case LoadTransformation::kS128Load32x2S:
734       opcode = kArm64S128Load32x2S;
735       break;
736     case LoadTransformation::kS128Load32x2U:
737       opcode = kArm64S128Load32x2U;
738       break;
739     case LoadTransformation::kS128Load32Zero:
740       opcode = kArm64LdrS;
741       break;
742     case LoadTransformation::kS128Load64Zero:
743       opcode = kArm64LdrD;
744       break;
745     default:
746       UNIMPLEMENTED();
747   }
748   // ARM64 supports unaligned loads
749   DCHECK_NE(params.kind, MemoryAccessKind::kUnaligned);
750 
751   Arm64OperandGenerator g(this);
752   Node* base = node->InputAt(0);
753   Node* index = node->InputAt(1);
754   InstructionOperand inputs[2];
755   InstructionOperand outputs[1];
756 
757   inputs[0] = g.UseRegister(base);
758   inputs[1] = g.UseRegister(index);
759   outputs[0] = g.DefineAsRegister(node);
760 
761   if (require_add) {
762     // ld1r uses post-index, so construct address first.
763     // TODO(v8:9886) If index can be immediate, use vldr without this add.
764     inputs[0] = EmitAddBeforeLoadOrStore(this, node, &opcode);
765     inputs[1] = g.TempImmediate(0);
766     opcode |= AddressingModeField::encode(kMode_MRI);
767   } else {
768     opcode |= AddressingModeField::encode(kMode_MRR);
769   }
770   if (params.kind == MemoryAccessKind::kProtected) {
771     opcode |= AccessModeField::encode(kMemoryAccessProtected);
772   }
773   Emit(opcode, 1, outputs, 2, inputs);
774 }
775 
VisitLoad(Node * node)776 void InstructionSelector::VisitLoad(Node* node) {
777   InstructionCode opcode = kArchNop;
778   ImmediateMode immediate_mode = kNoImmediate;
779   LoadRepresentation load_rep = LoadRepresentationOf(node->op());
780   MachineRepresentation rep = load_rep.representation();
781   switch (rep) {
782     case MachineRepresentation::kFloat32:
783       opcode = kArm64LdrS;
784       immediate_mode = kLoadStoreImm32;
785       break;
786     case MachineRepresentation::kFloat64:
787       opcode = kArm64LdrD;
788       immediate_mode = kLoadStoreImm64;
789       break;
790     case MachineRepresentation::kBit:  // Fall through.
791     case MachineRepresentation::kWord8:
792       opcode = load_rep.IsUnsigned()
793                    ? kArm64Ldrb
794                    : load_rep.semantic() == MachineSemantic::kInt32
795                          ? kArm64LdrsbW
796                          : kArm64Ldrsb;
797       immediate_mode = kLoadStoreImm8;
798       break;
799     case MachineRepresentation::kWord16:
800       opcode = load_rep.IsUnsigned()
801                    ? kArm64Ldrh
802                    : load_rep.semantic() == MachineSemantic::kInt32
803                          ? kArm64LdrshW
804                          : kArm64Ldrsh;
805       immediate_mode = kLoadStoreImm16;
806       break;
807     case MachineRepresentation::kWord32:
808       opcode = kArm64LdrW;
809       immediate_mode = kLoadStoreImm32;
810       break;
811     case MachineRepresentation::kCompressedPointer:  // Fall through.
812     case MachineRepresentation::kCompressed:
813 #ifdef V8_COMPRESS_POINTERS
814       opcode = kArm64LdrW;
815       immediate_mode = kLoadStoreImm32;
816       break;
817 #else
818       UNREACHABLE();
819 #endif
820 #ifdef V8_COMPRESS_POINTERS
821     case MachineRepresentation::kTaggedSigned:
822       opcode = kArm64LdrDecompressTaggedSigned;
823       immediate_mode = kLoadStoreImm32;
824       break;
825     case MachineRepresentation::kTaggedPointer:
826       opcode = kArm64LdrDecompressTaggedPointer;
827       immediate_mode = kLoadStoreImm32;
828       break;
829     case MachineRepresentation::kTagged:
830       opcode = kArm64LdrDecompressAnyTagged;
831       immediate_mode = kLoadStoreImm32;
832       break;
833 #else
834     case MachineRepresentation::kTaggedSigned:   // Fall through.
835     case MachineRepresentation::kTaggedPointer:  // Fall through.
836     case MachineRepresentation::kTagged:         // Fall through.
837 #endif
838     case MachineRepresentation::kWord64:
839       opcode = kArm64Ldr;
840       immediate_mode = kLoadStoreImm64;
841       break;
842     case MachineRepresentation::kSandboxedPointer:
843       opcode = kArm64LdrDecodeSandboxedPointer;
844       immediate_mode = kLoadStoreImm64;
845       break;
846     case MachineRepresentation::kSimd128:
847       opcode = kArm64LdrQ;
848       immediate_mode = kNoImmediate;
849       break;
850     case MachineRepresentation::kMapWord:  // Fall through.
851     case MachineRepresentation::kNone:
852       UNREACHABLE();
853   }
854   if (node->opcode() == IrOpcode::kProtectedLoad) {
855     opcode |= AccessModeField::encode(kMemoryAccessProtected);
856   }
857 
858   EmitLoad(this, node, opcode, immediate_mode, rep);
859 }
860 
VisitProtectedLoad(Node * node)861 void InstructionSelector::VisitProtectedLoad(Node* node) { VisitLoad(node); }
862 
VisitStore(Node * node)863 void InstructionSelector::VisitStore(Node* node) {
864   Arm64OperandGenerator g(this);
865   Node* base = node->InputAt(0);
866   Node* index = node->InputAt(1);
867   Node* value = node->InputAt(2);
868 
869   StoreRepresentation store_rep = StoreRepresentationOf(node->op());
870   WriteBarrierKind write_barrier_kind = store_rep.write_barrier_kind();
871   MachineRepresentation rep = store_rep.representation();
872 
873   if (FLAG_enable_unconditional_write_barriers &&
874       CanBeTaggedOrCompressedPointer(rep)) {
875     write_barrier_kind = kFullWriteBarrier;
876   }
877 
878   // TODO(arm64): I guess this could be done in a better way.
879   if (write_barrier_kind != kNoWriteBarrier && !FLAG_disable_write_barriers) {
880     DCHECK(CanBeTaggedOrCompressedPointer(rep));
881     AddressingMode addressing_mode;
882     InstructionOperand inputs[3];
883     size_t input_count = 0;
884     inputs[input_count++] = g.UseUniqueRegister(base);
885     // OutOfLineRecordWrite uses the index in an add or sub instruction, but we
886     // can trust the assembler to generate extra instructions if the index does
887     // not fit into add or sub. So here only check the immediate for a store.
888     if (g.CanBeImmediate(index, COMPRESS_POINTERS_BOOL ? kLoadStoreImm32
889                                                        : kLoadStoreImm64)) {
890       inputs[input_count++] = g.UseImmediate(index);
891       addressing_mode = kMode_MRI;
892     } else {
893       inputs[input_count++] = g.UseUniqueRegister(index);
894       addressing_mode = kMode_MRR;
895     }
896     inputs[input_count++] = g.UseUniqueRegister(value);
897     RecordWriteMode record_write_mode =
898         WriteBarrierKindToRecordWriteMode(write_barrier_kind);
899     InstructionCode code = kArchStoreWithWriteBarrier;
900     code |= AddressingModeField::encode(addressing_mode);
901     code |= MiscField::encode(static_cast<int>(record_write_mode));
902     Emit(code, 0, nullptr, input_count, inputs);
903   } else {
904     InstructionOperand inputs[4];
905     size_t input_count = 0;
906     InstructionCode opcode = kArchNop;
907     ImmediateMode immediate_mode = kNoImmediate;
908     switch (rep) {
909       case MachineRepresentation::kFloat32:
910         opcode = kArm64StrS;
911         immediate_mode = kLoadStoreImm32;
912         break;
913       case MachineRepresentation::kFloat64:
914         opcode = kArm64StrD;
915         immediate_mode = kLoadStoreImm64;
916         break;
917       case MachineRepresentation::kBit:  // Fall through.
918       case MachineRepresentation::kWord8:
919         opcode = kArm64Strb;
920         immediate_mode = kLoadStoreImm8;
921         break;
922       case MachineRepresentation::kWord16:
923         opcode = kArm64Strh;
924         immediate_mode = kLoadStoreImm16;
925         break;
926       case MachineRepresentation::kWord32:
927         opcode = kArm64StrW;
928         immediate_mode = kLoadStoreImm32;
929         break;
930       case MachineRepresentation::kCompressedPointer:  // Fall through.
931       case MachineRepresentation::kCompressed:
932 #ifdef V8_COMPRESS_POINTERS
933         opcode = kArm64StrCompressTagged;
934         immediate_mode = kLoadStoreImm32;
935         break;
936 #else
937         UNREACHABLE();
938 #endif
939       case MachineRepresentation::kTaggedSigned:   // Fall through.
940       case MachineRepresentation::kTaggedPointer:  // Fall through.
941       case MachineRepresentation::kTagged:
942         opcode = kArm64StrCompressTagged;
943         immediate_mode =
944             COMPRESS_POINTERS_BOOL ? kLoadStoreImm32 : kLoadStoreImm64;
945         break;
946       case MachineRepresentation::kSandboxedPointer:
947         opcode = kArm64StrEncodeSandboxedPointer;
948         immediate_mode = kLoadStoreImm64;
949         break;
950       case MachineRepresentation::kWord64:
951         opcode = kArm64Str;
952         immediate_mode = kLoadStoreImm64;
953         break;
954       case MachineRepresentation::kSimd128:
955         opcode = kArm64StrQ;
956         immediate_mode = kNoImmediate;
957         break;
958       case MachineRepresentation::kMapWord:  // Fall through.
959       case MachineRepresentation::kNone:
960         UNREACHABLE();
961     }
962 
963     ExternalReferenceMatcher m(base);
964     if (m.HasResolvedValue() && g.IsIntegerConstant(index) &&
965         CanAddressRelativeToRootsRegister(m.ResolvedValue())) {
966       ptrdiff_t const delta =
967           g.GetIntegerConstantValue(index) +
968           TurboAssemblerBase::RootRegisterOffsetForExternalReference(
969               isolate(), m.ResolvedValue());
970       if (is_int32(delta)) {
971         input_count = 2;
972         InstructionOperand inputs[2];
973         inputs[0] = g.UseRegister(value);
974         inputs[1] = g.UseImmediate(static_cast<int32_t>(delta));
975         opcode |= AddressingModeField::encode(kMode_Root);
976         Emit(opcode, 0, nullptr, input_count, inputs);
977         return;
978       }
979     }
980 
981     inputs[0] = g.UseRegisterOrImmediateZero(value);
982     inputs[1] = g.UseRegister(base);
983 
984     if (g.CanBeImmediate(index, immediate_mode)) {
985       input_count = 3;
986       inputs[2] = g.UseImmediate(index);
987       opcode |= AddressingModeField::encode(kMode_MRI);
988     } else if (TryMatchLoadStoreShift(&g, this, rep, node, index, &inputs[2],
989                                       &inputs[3])) {
990       input_count = 4;
991       opcode |= AddressingModeField::encode(kMode_Operand2_R_LSL_I);
992     } else {
993       input_count = 3;
994       inputs[2] = g.UseRegister(index);
995       opcode |= AddressingModeField::encode(kMode_MRR);
996     }
997 
998     if (node->opcode() == IrOpcode::kProtectedStore) {
999       opcode |= AccessModeField::encode(kMemoryAccessProtected);
1000     }
1001 
1002     Emit(opcode, 0, nullptr, input_count, inputs);
1003   }
1004 }
1005 
VisitProtectedStore(Node * node)1006 void InstructionSelector::VisitProtectedStore(Node* node) { VisitStore(node); }
1007 
VisitSimd128ReverseBytes(Node * node)1008 void InstructionSelector::VisitSimd128ReverseBytes(Node* node) {
1009   UNREACHABLE();
1010 }
1011 
1012 // Architecture supports unaligned access, therefore VisitLoad is used instead
VisitUnalignedLoad(Node * node)1013 void InstructionSelector::VisitUnalignedLoad(Node* node) { UNREACHABLE(); }
1014 
1015 // Architecture supports unaligned access, therefore VisitStore is used instead
VisitUnalignedStore(Node * node)1016 void InstructionSelector::VisitUnalignedStore(Node* node) { UNREACHABLE(); }
1017 
1018 template <typename Matcher>
VisitLogical(InstructionSelector * selector,Node * node,Matcher * m,ArchOpcode opcode,bool left_can_cover,bool right_can_cover,ImmediateMode imm_mode)1019 static void VisitLogical(InstructionSelector* selector, Node* node, Matcher* m,
1020                          ArchOpcode opcode, bool left_can_cover,
1021                          bool right_can_cover, ImmediateMode imm_mode) {
1022   Arm64OperandGenerator g(selector);
1023 
1024   // Map instruction to equivalent operation with inverted right input.
1025   ArchOpcode inv_opcode = opcode;
1026   switch (opcode) {
1027     case kArm64And32:
1028       inv_opcode = kArm64Bic32;
1029       break;
1030     case kArm64And:
1031       inv_opcode = kArm64Bic;
1032       break;
1033     case kArm64Or32:
1034       inv_opcode = kArm64Orn32;
1035       break;
1036     case kArm64Or:
1037       inv_opcode = kArm64Orn;
1038       break;
1039     case kArm64Eor32:
1040       inv_opcode = kArm64Eon32;
1041       break;
1042     case kArm64Eor:
1043       inv_opcode = kArm64Eon;
1044       break;
1045     default:
1046       UNREACHABLE();
1047   }
1048 
1049   // Select Logical(y, ~x) for Logical(Xor(x, -1), y).
1050   if ((m->left().IsWord32Xor() || m->left().IsWord64Xor()) && left_can_cover) {
1051     Matcher mleft(m->left().node());
1052     if (mleft.right().Is(-1)) {
1053       // TODO(all): support shifted operand on right.
1054       selector->Emit(inv_opcode, g.DefineAsRegister(node),
1055                      g.UseRegister(m->right().node()),
1056                      g.UseRegister(mleft.left().node()));
1057       return;
1058     }
1059   }
1060 
1061   // Select Logical(x, ~y) for Logical(x, Xor(y, -1)).
1062   if ((m->right().IsWord32Xor() || m->right().IsWord64Xor()) &&
1063       right_can_cover) {
1064     Matcher mright(m->right().node());
1065     if (mright.right().Is(-1)) {
1066       // TODO(all): support shifted operand on right.
1067       selector->Emit(inv_opcode, g.DefineAsRegister(node),
1068                      g.UseRegister(m->left().node()),
1069                      g.UseRegister(mright.left().node()));
1070       return;
1071     }
1072   }
1073 
1074   if (m->IsWord32Xor() && m->right().Is(-1)) {
1075     selector->Emit(kArm64Not32, g.DefineAsRegister(node),
1076                    g.UseRegister(m->left().node()));
1077   } else if (m->IsWord64Xor() && m->right().Is(-1)) {
1078     selector->Emit(kArm64Not, g.DefineAsRegister(node),
1079                    g.UseRegister(m->left().node()));
1080   } else {
1081     VisitBinop<Matcher>(selector, node, opcode, imm_mode);
1082   }
1083 }
1084 
VisitWord32And(Node * node)1085 void InstructionSelector::VisitWord32And(Node* node) {
1086   Arm64OperandGenerator g(this);
1087   Int32BinopMatcher m(node);
1088   if (m.left().IsWord32Shr() && CanCover(node, m.left().node()) &&
1089       m.right().HasResolvedValue()) {
1090     uint32_t mask = m.right().ResolvedValue();
1091     uint32_t mask_width = base::bits::CountPopulation(mask);
1092     uint32_t mask_msb = base::bits::CountLeadingZeros32(mask);
1093     if ((mask_width != 0) && (mask_width != 32) &&
1094         (mask_msb + mask_width == 32)) {
1095       // The mask must be contiguous, and occupy the least-significant bits.
1096       DCHECK_EQ(0u, base::bits::CountTrailingZeros32(mask));
1097 
1098       // Select Ubfx for And(Shr(x, imm), mask) where the mask is in the least
1099       // significant bits.
1100       Int32BinopMatcher mleft(m.left().node());
1101       if (mleft.right().HasResolvedValue()) {
1102         // Any shift value can match; int32 shifts use `value % 32`.
1103         uint32_t lsb = mleft.right().ResolvedValue() & 0x1F;
1104 
1105         // Ubfx cannot extract bits past the register size, however since
1106         // shifting the original value would have introduced some zeros we can
1107         // still use ubfx with a smaller mask and the remaining bits will be
1108         // zeros.
1109         if (lsb + mask_width > 32) mask_width = 32 - lsb;
1110 
1111         Emit(kArm64Ubfx32, g.DefineAsRegister(node),
1112              g.UseRegister(mleft.left().node()),
1113              g.UseImmediateOrTemp(mleft.right().node(), lsb),
1114              g.TempImmediate(mask_width));
1115         return;
1116       }
1117       // Other cases fall through to the normal And operation.
1118     }
1119   }
1120   VisitLogical<Int32BinopMatcher>(
1121       this, node, &m, kArm64And32, CanCover(node, m.left().node()),
1122       CanCover(node, m.right().node()), kLogical32Imm);
1123 }
1124 
VisitWord64And(Node * node)1125 void InstructionSelector::VisitWord64And(Node* node) {
1126   Arm64OperandGenerator g(this);
1127   Int64BinopMatcher m(node);
1128   if (m.left().IsWord64Shr() && CanCover(node, m.left().node()) &&
1129       m.right().HasResolvedValue()) {
1130     uint64_t mask = m.right().ResolvedValue();
1131     uint64_t mask_width = base::bits::CountPopulation(mask);
1132     uint64_t mask_msb = base::bits::CountLeadingZeros64(mask);
1133     if ((mask_width != 0) && (mask_width != 64) &&
1134         (mask_msb + mask_width == 64)) {
1135       // The mask must be contiguous, and occupy the least-significant bits.
1136       DCHECK_EQ(0u, base::bits::CountTrailingZeros64(mask));
1137 
1138       // Select Ubfx for And(Shr(x, imm), mask) where the mask is in the least
1139       // significant bits.
1140       Int64BinopMatcher mleft(m.left().node());
1141       if (mleft.right().HasResolvedValue()) {
1142         // Any shift value can match; int64 shifts use `value % 64`.
1143         uint32_t lsb =
1144             static_cast<uint32_t>(mleft.right().ResolvedValue() & 0x3F);
1145 
1146         // Ubfx cannot extract bits past the register size, however since
1147         // shifting the original value would have introduced some zeros we can
1148         // still use ubfx with a smaller mask and the remaining bits will be
1149         // zeros.
1150         if (lsb + mask_width > 64) mask_width = 64 - lsb;
1151 
1152         Emit(kArm64Ubfx, g.DefineAsRegister(node),
1153              g.UseRegister(mleft.left().node()),
1154              g.UseImmediateOrTemp(mleft.right().node(), lsb),
1155              g.TempImmediate(static_cast<int32_t>(mask_width)));
1156         return;
1157       }
1158       // Other cases fall through to the normal And operation.
1159     }
1160   }
1161   VisitLogical<Int64BinopMatcher>(
1162       this, node, &m, kArm64And, CanCover(node, m.left().node()),
1163       CanCover(node, m.right().node()), kLogical64Imm);
1164 }
1165 
VisitWord32Or(Node * node)1166 void InstructionSelector::VisitWord32Or(Node* node) {
1167   Int32BinopMatcher m(node);
1168   VisitLogical<Int32BinopMatcher>(
1169       this, node, &m, kArm64Or32, CanCover(node, m.left().node()),
1170       CanCover(node, m.right().node()), kLogical32Imm);
1171 }
1172 
VisitWord64Or(Node * node)1173 void InstructionSelector::VisitWord64Or(Node* node) {
1174   Int64BinopMatcher m(node);
1175   VisitLogical<Int64BinopMatcher>(
1176       this, node, &m, kArm64Or, CanCover(node, m.left().node()),
1177       CanCover(node, m.right().node()), kLogical64Imm);
1178 }
1179 
VisitWord32Xor(Node * node)1180 void InstructionSelector::VisitWord32Xor(Node* node) {
1181   Int32BinopMatcher m(node);
1182   VisitLogical<Int32BinopMatcher>(
1183       this, node, &m, kArm64Eor32, CanCover(node, m.left().node()),
1184       CanCover(node, m.right().node()), kLogical32Imm);
1185 }
1186 
VisitWord64Xor(Node * node)1187 void InstructionSelector::VisitWord64Xor(Node* node) {
1188   Int64BinopMatcher m(node);
1189   VisitLogical<Int64BinopMatcher>(
1190       this, node, &m, kArm64Eor, CanCover(node, m.left().node()),
1191       CanCover(node, m.right().node()), kLogical64Imm);
1192 }
1193 
VisitWord32Shl(Node * node)1194 void InstructionSelector::VisitWord32Shl(Node* node) {
1195   Int32BinopMatcher m(node);
1196   if (m.left().IsWord32And() && CanCover(node, m.left().node()) &&
1197       m.right().IsInRange(1, 31)) {
1198     Arm64OperandGenerator g(this);
1199     Int32BinopMatcher mleft(m.left().node());
1200     if (mleft.right().HasResolvedValue()) {
1201       uint32_t mask = mleft.right().ResolvedValue();
1202       uint32_t mask_width = base::bits::CountPopulation(mask);
1203       uint32_t mask_msb = base::bits::CountLeadingZeros32(mask);
1204       if ((mask_width != 0) && (mask_msb + mask_width == 32)) {
1205         uint32_t shift = m.right().ResolvedValue();
1206         DCHECK_EQ(0u, base::bits::CountTrailingZeros32(mask));
1207         DCHECK_NE(0u, shift);
1208 
1209         if ((shift + mask_width) >= 32) {
1210           // If the mask is contiguous and reaches or extends beyond the top
1211           // bit, only the shift is needed.
1212           Emit(kArm64Lsl32, g.DefineAsRegister(node),
1213                g.UseRegister(mleft.left().node()),
1214                g.UseImmediate(m.right().node()));
1215           return;
1216         } else {
1217           // Select Ubfiz for Shl(And(x, mask), imm) where the mask is
1218           // contiguous, and the shift immediate non-zero.
1219           Emit(kArm64Ubfiz32, g.DefineAsRegister(node),
1220                g.UseRegister(mleft.left().node()),
1221                g.UseImmediate(m.right().node()), g.TempImmediate(mask_width));
1222           return;
1223         }
1224       }
1225     }
1226   }
1227   VisitRRO(this, kArm64Lsl32, node, kShift32Imm);
1228 }
1229 
VisitWord64Shl(Node * node)1230 void InstructionSelector::VisitWord64Shl(Node* node) {
1231   Arm64OperandGenerator g(this);
1232   Int64BinopMatcher m(node);
1233   if ((m.left().IsChangeInt32ToInt64() || m.left().IsChangeUint32ToUint64()) &&
1234       m.right().IsInRange(32, 63) && CanCover(node, m.left().node())) {
1235     // There's no need to sign/zero-extend to 64-bit if we shift out the upper
1236     // 32 bits anyway.
1237     Emit(kArm64Lsl, g.DefineAsRegister(node),
1238          g.UseRegister(m.left().node()->InputAt(0)),
1239          g.UseImmediate(m.right().node()));
1240     return;
1241   }
1242   VisitRRO(this, kArm64Lsl, node, kShift64Imm);
1243 }
1244 
VisitStackPointerGreaterThan(Node * node,FlagsContinuation * cont)1245 void InstructionSelector::VisitStackPointerGreaterThan(
1246     Node* node, FlagsContinuation* cont) {
1247   StackCheckKind kind = StackCheckKindOf(node->op());
1248   InstructionCode opcode =
1249       kArchStackPointerGreaterThan | MiscField::encode(static_cast<int>(kind));
1250 
1251   Arm64OperandGenerator g(this);
1252 
1253   // No outputs.
1254   InstructionOperand* const outputs = nullptr;
1255   const int output_count = 0;
1256 
1257   // Applying an offset to this stack check requires a temp register. Offsets
1258   // are only applied to the first stack check. If applying an offset, we must
1259   // ensure the input and temp registers do not alias, thus kUniqueRegister.
1260   InstructionOperand temps[] = {g.TempRegister()};
1261   const int temp_count = (kind == StackCheckKind::kJSFunctionEntry) ? 1 : 0;
1262   const auto register_mode = (kind == StackCheckKind::kJSFunctionEntry)
1263                                  ? OperandGenerator::kUniqueRegister
1264                                  : OperandGenerator::kRegister;
1265 
1266   Node* const value = node->InputAt(0);
1267   InstructionOperand inputs[] = {g.UseRegisterWithMode(value, register_mode)};
1268   static constexpr int input_count = arraysize(inputs);
1269 
1270   EmitWithContinuation(opcode, output_count, outputs, input_count, inputs,
1271                        temp_count, temps, cont);
1272 }
1273 
1274 namespace {
1275 
TryEmitBitfieldExtract32(InstructionSelector * selector,Node * node)1276 bool TryEmitBitfieldExtract32(InstructionSelector* selector, Node* node) {
1277   Arm64OperandGenerator g(selector);
1278   Int32BinopMatcher m(node);
1279   if (selector->CanCover(node, m.left().node()) && m.left().IsWord32Shl()) {
1280     // Select Ubfx or Sbfx for (x << (K & 0x1F)) OP (K & 0x1F), where
1281     // OP is >>> or >> and (K & 0x1F) != 0.
1282     Int32BinopMatcher mleft(m.left().node());
1283     if (mleft.right().HasResolvedValue() && m.right().HasResolvedValue() &&
1284         (mleft.right().ResolvedValue() & 0x1F) != 0 &&
1285         (mleft.right().ResolvedValue() & 0x1F) ==
1286             (m.right().ResolvedValue() & 0x1F)) {
1287       DCHECK(m.IsWord32Shr() || m.IsWord32Sar());
1288       ArchOpcode opcode = m.IsWord32Sar() ? kArm64Sbfx32 : kArm64Ubfx32;
1289 
1290       int right_val = m.right().ResolvedValue() & 0x1F;
1291       DCHECK_NE(right_val, 0);
1292 
1293       selector->Emit(opcode, g.DefineAsRegister(node),
1294                      g.UseRegister(mleft.left().node()), g.TempImmediate(0),
1295                      g.TempImmediate(32 - right_val));
1296       return true;
1297     }
1298   }
1299   return false;
1300 }
1301 
1302 }  // namespace
1303 
VisitWord32Shr(Node * node)1304 void InstructionSelector::VisitWord32Shr(Node* node) {
1305   Int32BinopMatcher m(node);
1306   if (m.left().IsWord32And() && m.right().HasResolvedValue()) {
1307     uint32_t lsb = m.right().ResolvedValue() & 0x1F;
1308     Int32BinopMatcher mleft(m.left().node());
1309     if (mleft.right().HasResolvedValue() &&
1310         mleft.right().ResolvedValue() != 0) {
1311       // Select Ubfx for Shr(And(x, mask), imm) where the result of the mask is
1312       // shifted into the least-significant bits.
1313       uint32_t mask =
1314           static_cast<uint32_t>(mleft.right().ResolvedValue() >> lsb) << lsb;
1315       unsigned mask_width = base::bits::CountPopulation(mask);
1316       unsigned mask_msb = base::bits::CountLeadingZeros32(mask);
1317       if ((mask_msb + mask_width + lsb) == 32) {
1318         Arm64OperandGenerator g(this);
1319         DCHECK_EQ(lsb, base::bits::CountTrailingZeros32(mask));
1320         Emit(kArm64Ubfx32, g.DefineAsRegister(node),
1321              g.UseRegister(mleft.left().node()),
1322              g.UseImmediateOrTemp(m.right().node(), lsb),
1323              g.TempImmediate(mask_width));
1324         return;
1325       }
1326     }
1327   } else if (TryEmitBitfieldExtract32(this, node)) {
1328     return;
1329   }
1330 
1331   if (m.left().IsUint32MulHigh() && m.right().HasResolvedValue() &&
1332       CanCover(node, node->InputAt(0))) {
1333     // Combine this shift with the multiply and shift that would be generated
1334     // by Uint32MulHigh.
1335     Arm64OperandGenerator g(this);
1336     Node* left = m.left().node();
1337     int shift = m.right().ResolvedValue() & 0x1F;
1338     InstructionOperand const smull_operand = g.TempRegister();
1339     Emit(kArm64Umull, smull_operand, g.UseRegister(left->InputAt(0)),
1340          g.UseRegister(left->InputAt(1)));
1341     Emit(kArm64Lsr, g.DefineAsRegister(node), smull_operand,
1342          g.TempImmediate(32 + shift));
1343     return;
1344   }
1345 
1346   VisitRRO(this, kArm64Lsr32, node, kShift32Imm);
1347 }
1348 
VisitWord64Shr(Node * node)1349 void InstructionSelector::VisitWord64Shr(Node* node) {
1350   Int64BinopMatcher m(node);
1351   if (m.left().IsWord64And() && m.right().HasResolvedValue()) {
1352     uint32_t lsb = m.right().ResolvedValue() & 0x3F;
1353     Int64BinopMatcher mleft(m.left().node());
1354     if (mleft.right().HasResolvedValue() &&
1355         mleft.right().ResolvedValue() != 0) {
1356       // Select Ubfx for Shr(And(x, mask), imm) where the result of the mask is
1357       // shifted into the least-significant bits.
1358       uint64_t mask =
1359           static_cast<uint64_t>(mleft.right().ResolvedValue() >> lsb) << lsb;
1360       unsigned mask_width = base::bits::CountPopulation(mask);
1361       unsigned mask_msb = base::bits::CountLeadingZeros64(mask);
1362       if ((mask_msb + mask_width + lsb) == 64) {
1363         Arm64OperandGenerator g(this);
1364         DCHECK_EQ(lsb, base::bits::CountTrailingZeros64(mask));
1365         Emit(kArm64Ubfx, g.DefineAsRegister(node),
1366              g.UseRegister(mleft.left().node()),
1367              g.UseImmediateOrTemp(m.right().node(), lsb),
1368              g.TempImmediate(mask_width));
1369         return;
1370       }
1371     }
1372   }
1373   VisitRRO(this, kArm64Lsr, node, kShift64Imm);
1374 }
1375 
VisitWord32Sar(Node * node)1376 void InstructionSelector::VisitWord32Sar(Node* node) {
1377   if (TryEmitBitfieldExtract32(this, node)) {
1378     return;
1379   }
1380 
1381   Int32BinopMatcher m(node);
1382   if (m.left().IsInt32MulHigh() && m.right().HasResolvedValue() &&
1383       CanCover(node, node->InputAt(0))) {
1384     // Combine this shift with the multiply and shift that would be generated
1385     // by Int32MulHigh.
1386     Arm64OperandGenerator g(this);
1387     Node* left = m.left().node();
1388     int shift = m.right().ResolvedValue() & 0x1F;
1389     InstructionOperand const smull_operand = g.TempRegister();
1390     Emit(kArm64Smull, smull_operand, g.UseRegister(left->InputAt(0)),
1391          g.UseRegister(left->InputAt(1)));
1392     Emit(kArm64Asr, g.DefineAsRegister(node), smull_operand,
1393          g.TempImmediate(32 + shift));
1394     return;
1395   }
1396 
1397   if (m.left().IsInt32Add() && m.right().HasResolvedValue() &&
1398       CanCover(node, node->InputAt(0))) {
1399     Node* add_node = m.left().node();
1400     Int32BinopMatcher madd_node(add_node);
1401     if (madd_node.left().IsInt32MulHigh() &&
1402         CanCover(add_node, madd_node.left().node())) {
1403       // Combine the shift that would be generated by Int32MulHigh with the add
1404       // on the left of this Sar operation. We do it here, as the result of the
1405       // add potentially has 33 bits, so we have to ensure the result is
1406       // truncated by being the input to this 32-bit Sar operation.
1407       Arm64OperandGenerator g(this);
1408       Node* mul_node = madd_node.left().node();
1409 
1410       InstructionOperand const smull_operand = g.TempRegister();
1411       Emit(kArm64Smull, smull_operand, g.UseRegister(mul_node->InputAt(0)),
1412            g.UseRegister(mul_node->InputAt(1)));
1413 
1414       InstructionOperand const add_operand = g.TempRegister();
1415       Emit(kArm64Add | AddressingModeField::encode(kMode_Operand2_R_ASR_I),
1416            add_operand, g.UseRegister(add_node->InputAt(1)), smull_operand,
1417            g.TempImmediate(32));
1418 
1419       Emit(kArm64Asr32, g.DefineAsRegister(node), add_operand,
1420            g.UseImmediate(node->InputAt(1)));
1421       return;
1422     }
1423   }
1424 
1425   VisitRRO(this, kArm64Asr32, node, kShift32Imm);
1426 }
1427 
VisitWord64Sar(Node * node)1428 void InstructionSelector::VisitWord64Sar(Node* node) {
1429   if (TryEmitExtendingLoad(this, node)) return;
1430   VisitRRO(this, kArm64Asr, node, kShift64Imm);
1431 }
1432 
VisitWord32Rol(Node * node)1433 void InstructionSelector::VisitWord32Rol(Node* node) { UNREACHABLE(); }
1434 
VisitWord64Rol(Node * node)1435 void InstructionSelector::VisitWord64Rol(Node* node) { UNREACHABLE(); }
1436 
VisitWord32Ror(Node * node)1437 void InstructionSelector::VisitWord32Ror(Node* node) {
1438   VisitRRO(this, kArm64Ror32, node, kShift32Imm);
1439 }
1440 
VisitWord64Ror(Node * node)1441 void InstructionSelector::VisitWord64Ror(Node* node) {
1442   VisitRRO(this, kArm64Ror, node, kShift64Imm);
1443 }
1444 
1445 #define RR_OP_LIST(V)                                         \
1446   V(Word64Clz, kArm64Clz)                                     \
1447   V(Word32Clz, kArm64Clz32)                                   \
1448   V(Word32Popcnt, kArm64Cnt32)                                \
1449   V(Word64Popcnt, kArm64Cnt64)                                \
1450   V(Word32ReverseBits, kArm64Rbit32)                          \
1451   V(Word64ReverseBits, kArm64Rbit)                            \
1452   V(Word32ReverseBytes, kArm64Rev32)                          \
1453   V(Word64ReverseBytes, kArm64Rev)                            \
1454   V(ChangeFloat32ToFloat64, kArm64Float32ToFloat64)           \
1455   V(RoundInt32ToFloat32, kArm64Int32ToFloat32)                \
1456   V(RoundUint32ToFloat32, kArm64Uint32ToFloat32)              \
1457   V(ChangeInt32ToFloat64, kArm64Int32ToFloat64)               \
1458   V(ChangeInt64ToFloat64, kArm64Int64ToFloat64)               \
1459   V(ChangeUint32ToFloat64, kArm64Uint32ToFloat64)             \
1460   V(ChangeFloat64ToInt32, kArm64Float64ToInt32)               \
1461   V(ChangeFloat64ToInt64, kArm64Float64ToInt64)               \
1462   V(ChangeFloat64ToUint32, kArm64Float64ToUint32)             \
1463   V(ChangeFloat64ToUint64, kArm64Float64ToUint64)             \
1464   V(TruncateFloat64ToUint32, kArm64Float64ToUint32)           \
1465   V(TruncateFloat64ToFloat32, kArm64Float64ToFloat32)         \
1466   V(TruncateFloat64ToWord32, kArchTruncateDoubleToI)          \
1467   V(RoundFloat64ToInt32, kArm64Float64ToInt32)                \
1468   V(RoundInt64ToFloat32, kArm64Int64ToFloat32)                \
1469   V(RoundInt64ToFloat64, kArm64Int64ToFloat64)                \
1470   V(RoundUint64ToFloat32, kArm64Uint64ToFloat32)              \
1471   V(RoundUint64ToFloat64, kArm64Uint64ToFloat64)              \
1472   V(BitcastFloat32ToInt32, kArm64Float64ExtractLowWord32)     \
1473   V(BitcastFloat64ToInt64, kArm64U64MoveFloat64)              \
1474   V(BitcastInt32ToFloat32, kArm64Float64MoveU64)              \
1475   V(BitcastInt64ToFloat64, kArm64Float64MoveU64)              \
1476   V(Float32Sqrt, kArm64Float32Sqrt)                           \
1477   V(Float64Sqrt, kArm64Float64Sqrt)                           \
1478   V(Float32RoundDown, kArm64Float32RoundDown)                 \
1479   V(Float64RoundDown, kArm64Float64RoundDown)                 \
1480   V(Float32RoundUp, kArm64Float32RoundUp)                     \
1481   V(Float64RoundUp, kArm64Float64RoundUp)                     \
1482   V(Float32RoundTruncate, kArm64Float32RoundTruncate)         \
1483   V(Float64RoundTruncate, kArm64Float64RoundTruncate)         \
1484   V(Float64RoundTiesAway, kArm64Float64RoundTiesAway)         \
1485   V(Float32RoundTiesEven, kArm64Float32RoundTiesEven)         \
1486   V(Float64RoundTiesEven, kArm64Float64RoundTiesEven)         \
1487   V(Float64ExtractLowWord32, kArm64Float64ExtractLowWord32)   \
1488   V(Float64ExtractHighWord32, kArm64Float64ExtractHighWord32) \
1489   V(Float64SilenceNaN, kArm64Float64SilenceNaN)               \
1490   V(F32x4Ceil, kArm64Float32RoundUp)                          \
1491   V(F32x4Floor, kArm64Float32RoundDown)                       \
1492   V(F32x4Trunc, kArm64Float32RoundTruncate)                   \
1493   V(F32x4NearestInt, kArm64Float32RoundTiesEven)              \
1494   V(F64x2Ceil, kArm64Float64RoundUp)                          \
1495   V(F64x2Floor, kArm64Float64RoundDown)                       \
1496   V(F64x2Trunc, kArm64Float64RoundTruncate)                   \
1497   V(F64x2NearestInt, kArm64Float64RoundTiesEven)
1498 
1499 #define RRR_OP_LIST(V)            \
1500   V(Int32Div, kArm64Idiv32)       \
1501   V(Int64Div, kArm64Idiv)         \
1502   V(Uint32Div, kArm64Udiv32)      \
1503   V(Uint64Div, kArm64Udiv)        \
1504   V(Int32Mod, kArm64Imod32)       \
1505   V(Int64Mod, kArm64Imod)         \
1506   V(Uint32Mod, kArm64Umod32)      \
1507   V(Uint64Mod, kArm64Umod)        \
1508   V(Float32Add, kArm64Float32Add) \
1509   V(Float64Add, kArm64Float64Add) \
1510   V(Float32Sub, kArm64Float32Sub) \
1511   V(Float64Sub, kArm64Float64Sub) \
1512   V(Float32Div, kArm64Float32Div) \
1513   V(Float64Div, kArm64Float64Div) \
1514   V(Float32Max, kArm64Float32Max) \
1515   V(Float64Max, kArm64Float64Max) \
1516   V(Float32Min, kArm64Float32Min) \
1517   V(Float64Min, kArm64Float64Min) \
1518   V(I8x16Swizzle, kArm64I8x16Swizzle)
1519 
1520 #define RR_VISITOR(Name, opcode)                      \
1521   void InstructionSelector::Visit##Name(Node* node) { \
1522     VisitRR(this, opcode, node);                      \
1523   }
1524 RR_OP_LIST(RR_VISITOR)
1525 #undef RR_VISITOR
1526 #undef RR_OP_LIST
1527 
1528 #define RRR_VISITOR(Name, opcode)                     \
1529   void InstructionSelector::Visit##Name(Node* node) { \
1530     VisitRRR(this, opcode, node);                     \
1531   }
RRR_OP_LIST(RRR_VISITOR)1532 RRR_OP_LIST(RRR_VISITOR)
1533 #undef RRR_VISITOR
1534 #undef RRR_OP_LIST
1535 
1536 void InstructionSelector::VisitWord32Ctz(Node* node) { UNREACHABLE(); }
1537 
VisitWord64Ctz(Node * node)1538 void InstructionSelector::VisitWord64Ctz(Node* node) { UNREACHABLE(); }
1539 
VisitInt32Add(Node * node)1540 void InstructionSelector::VisitInt32Add(Node* node) {
1541   Arm64OperandGenerator g(this);
1542   Int32BinopMatcher m(node);
1543   // Select Madd(x, y, z) for Add(Mul(x, y), z).
1544   if (m.left().IsInt32Mul() && CanCover(node, m.left().node())) {
1545     Int32BinopMatcher mleft(m.left().node());
1546     // Check multiply can't be later reduced to addition with shift.
1547     if (LeftShiftForReducedMultiply(&mleft) == 0) {
1548       Emit(kArm64Madd32, g.DefineAsRegister(node),
1549            g.UseRegister(mleft.left().node()),
1550            g.UseRegister(mleft.right().node()),
1551            g.UseRegister(m.right().node()));
1552       return;
1553     }
1554   }
1555   // Select Madd(x, y, z) for Add(z, Mul(x, y)).
1556   if (m.right().IsInt32Mul() && CanCover(node, m.right().node())) {
1557     Int32BinopMatcher mright(m.right().node());
1558     // Check multiply can't be later reduced to addition with shift.
1559     if (LeftShiftForReducedMultiply(&mright) == 0) {
1560       Emit(kArm64Madd32, g.DefineAsRegister(node),
1561            g.UseRegister(mright.left().node()),
1562            g.UseRegister(mright.right().node()),
1563            g.UseRegister(m.left().node()));
1564       return;
1565     }
1566   }
1567   VisitAddSub<Int32BinopMatcher>(this, node, kArm64Add32, kArm64Sub32);
1568 }
1569 
VisitInt64Add(Node * node)1570 void InstructionSelector::VisitInt64Add(Node* node) {
1571   Arm64OperandGenerator g(this);
1572   Int64BinopMatcher m(node);
1573   // Select Madd(x, y, z) for Add(Mul(x, y), z).
1574   if (m.left().IsInt64Mul() && CanCover(node, m.left().node())) {
1575     Int64BinopMatcher mleft(m.left().node());
1576     // Check multiply can't be later reduced to addition with shift.
1577     if (LeftShiftForReducedMultiply(&mleft) == 0) {
1578       Emit(kArm64Madd, g.DefineAsRegister(node),
1579            g.UseRegister(mleft.left().node()),
1580            g.UseRegister(mleft.right().node()),
1581            g.UseRegister(m.right().node()));
1582       return;
1583     }
1584   }
1585   // Select Madd(x, y, z) for Add(z, Mul(x, y)).
1586   if (m.right().IsInt64Mul() && CanCover(node, m.right().node())) {
1587     Int64BinopMatcher mright(m.right().node());
1588     // Check multiply can't be later reduced to addition with shift.
1589     if (LeftShiftForReducedMultiply(&mright) == 0) {
1590       Emit(kArm64Madd, g.DefineAsRegister(node),
1591            g.UseRegister(mright.left().node()),
1592            g.UseRegister(mright.right().node()),
1593            g.UseRegister(m.left().node()));
1594       return;
1595     }
1596   }
1597   VisitAddSub<Int64BinopMatcher>(this, node, kArm64Add, kArm64Sub);
1598 }
1599 
VisitInt32Sub(Node * node)1600 void InstructionSelector::VisitInt32Sub(Node* node) {
1601   Arm64OperandGenerator g(this);
1602   Int32BinopMatcher m(node);
1603 
1604   // Select Msub(x, y, a) for Sub(a, Mul(x, y)).
1605   if (m.right().IsInt32Mul() && CanCover(node, m.right().node())) {
1606     Int32BinopMatcher mright(m.right().node());
1607     // Check multiply can't be later reduced to addition with shift.
1608     if (LeftShiftForReducedMultiply(&mright) == 0) {
1609       Emit(kArm64Msub32, g.DefineAsRegister(node),
1610            g.UseRegister(mright.left().node()),
1611            g.UseRegister(mright.right().node()),
1612            g.UseRegister(m.left().node()));
1613       return;
1614     }
1615   }
1616 
1617   VisitAddSub<Int32BinopMatcher>(this, node, kArm64Sub32, kArm64Add32);
1618 }
1619 
VisitInt64Sub(Node * node)1620 void InstructionSelector::VisitInt64Sub(Node* node) {
1621   Arm64OperandGenerator g(this);
1622   Int64BinopMatcher m(node);
1623 
1624   // Select Msub(x, y, a) for Sub(a, Mul(x, y)).
1625   if (m.right().IsInt64Mul() && CanCover(node, m.right().node())) {
1626     Int64BinopMatcher mright(m.right().node());
1627     // Check multiply can't be later reduced to addition with shift.
1628     if (LeftShiftForReducedMultiply(&mright) == 0) {
1629       Emit(kArm64Msub, g.DefineAsRegister(node),
1630            g.UseRegister(mright.left().node()),
1631            g.UseRegister(mright.right().node()),
1632            g.UseRegister(m.left().node()));
1633       return;
1634     }
1635   }
1636 
1637   VisitAddSub<Int64BinopMatcher>(this, node, kArm64Sub, kArm64Add);
1638 }
1639 
1640 namespace {
1641 
EmitInt32MulWithOverflow(InstructionSelector * selector,Node * node,FlagsContinuation * cont)1642 void EmitInt32MulWithOverflow(InstructionSelector* selector, Node* node,
1643                               FlagsContinuation* cont) {
1644   Arm64OperandGenerator g(selector);
1645   Int32BinopMatcher m(node);
1646   InstructionOperand result = g.DefineAsRegister(node);
1647   InstructionOperand left = g.UseRegister(m.left().node());
1648   InstructionOperand right = g.UseRegister(m.right().node());
1649   selector->Emit(kArm64Smull, result, left, right);
1650 
1651   InstructionCode opcode =
1652       kArm64Cmp | AddressingModeField::encode(kMode_Operand2_R_SXTW);
1653   selector->EmitWithContinuation(opcode, result, result, cont);
1654 }
1655 
1656 }  // namespace
1657 
VisitInt32Mul(Node * node)1658 void InstructionSelector::VisitInt32Mul(Node* node) {
1659   Arm64OperandGenerator g(this);
1660   Int32BinopMatcher m(node);
1661 
1662   // First, try to reduce the multiplication to addition with left shift.
1663   // x * (2^k + 1) -> x + (x << k)
1664   int32_t shift = LeftShiftForReducedMultiply(&m);
1665   if (shift > 0) {
1666     Emit(kArm64Add32 | AddressingModeField::encode(kMode_Operand2_R_LSL_I),
1667          g.DefineAsRegister(node), g.UseRegister(m.left().node()),
1668          g.UseRegister(m.left().node()), g.TempImmediate(shift));
1669     return;
1670   }
1671 
1672   if (m.left().IsInt32Sub() && CanCover(node, m.left().node())) {
1673     Int32BinopMatcher mleft(m.left().node());
1674 
1675     // Select Mneg(x, y) for Mul(Sub(0, x), y).
1676     if (mleft.left().Is(0)) {
1677       Emit(kArm64Mneg32, g.DefineAsRegister(node),
1678            g.UseRegister(mleft.right().node()),
1679            g.UseRegister(m.right().node()));
1680       return;
1681     }
1682   }
1683 
1684   if (m.right().IsInt32Sub() && CanCover(node, m.right().node())) {
1685     Int32BinopMatcher mright(m.right().node());
1686 
1687     // Select Mneg(x, y) for Mul(x, Sub(0, y)).
1688     if (mright.left().Is(0)) {
1689       Emit(kArm64Mneg32, g.DefineAsRegister(node),
1690            g.UseRegister(m.left().node()),
1691            g.UseRegister(mright.right().node()));
1692       return;
1693     }
1694   }
1695 
1696   VisitRRR(this, kArm64Mul32, node);
1697 }
1698 
VisitInt64Mul(Node * node)1699 void InstructionSelector::VisitInt64Mul(Node* node) {
1700   Arm64OperandGenerator g(this);
1701   Int64BinopMatcher m(node);
1702 
1703   // First, try to reduce the multiplication to addition with left shift.
1704   // x * (2^k + 1) -> x + (x << k)
1705   int32_t shift = LeftShiftForReducedMultiply(&m);
1706   if (shift > 0) {
1707     Emit(kArm64Add | AddressingModeField::encode(kMode_Operand2_R_LSL_I),
1708          g.DefineAsRegister(node), g.UseRegister(m.left().node()),
1709          g.UseRegister(m.left().node()), g.TempImmediate(shift));
1710     return;
1711   }
1712 
1713   if (m.left().IsInt64Sub() && CanCover(node, m.left().node())) {
1714     Int64BinopMatcher mleft(m.left().node());
1715 
1716     // Select Mneg(x, y) for Mul(Sub(0, x), y).
1717     if (mleft.left().Is(0)) {
1718       Emit(kArm64Mneg, g.DefineAsRegister(node),
1719            g.UseRegister(mleft.right().node()),
1720            g.UseRegister(m.right().node()));
1721       return;
1722     }
1723   }
1724 
1725   if (m.right().IsInt64Sub() && CanCover(node, m.right().node())) {
1726     Int64BinopMatcher mright(m.right().node());
1727 
1728     // Select Mneg(x, y) for Mul(x, Sub(0, y)).
1729     if (mright.left().Is(0)) {
1730       Emit(kArm64Mneg, g.DefineAsRegister(node), g.UseRegister(m.left().node()),
1731            g.UseRegister(mright.right().node()));
1732       return;
1733     }
1734   }
1735 
1736   VisitRRR(this, kArm64Mul, node);
1737 }
1738 
1739 namespace {
VisitExtMul(InstructionSelector * selector,ArchOpcode opcode,Node * node,int dst_lane_size)1740 void VisitExtMul(InstructionSelector* selector, ArchOpcode opcode, Node* node,
1741                  int dst_lane_size) {
1742   InstructionCode code = opcode;
1743   code |= LaneSizeField::encode(dst_lane_size);
1744   VisitRRR(selector, code, node);
1745 }
1746 }  // namespace
1747 
VisitI16x8ExtMulLowI8x16S(Node * node)1748 void InstructionSelector::VisitI16x8ExtMulLowI8x16S(Node* node) {
1749   VisitExtMul(this, kArm64Smull, node, 16);
1750 }
1751 
VisitI16x8ExtMulHighI8x16S(Node * node)1752 void InstructionSelector::VisitI16x8ExtMulHighI8x16S(Node* node) {
1753   VisitExtMul(this, kArm64Smull2, node, 16);
1754 }
1755 
VisitI16x8ExtMulLowI8x16U(Node * node)1756 void InstructionSelector::VisitI16x8ExtMulLowI8x16U(Node* node) {
1757   VisitExtMul(this, kArm64Umull, node, 16);
1758 }
1759 
VisitI16x8ExtMulHighI8x16U(Node * node)1760 void InstructionSelector::VisitI16x8ExtMulHighI8x16U(Node* node) {
1761   VisitExtMul(this, kArm64Umull2, node, 16);
1762 }
1763 
VisitI32x4ExtMulLowI16x8S(Node * node)1764 void InstructionSelector::VisitI32x4ExtMulLowI16x8S(Node* node) {
1765   VisitExtMul(this, kArm64Smull, node, 32);
1766 }
1767 
VisitI32x4ExtMulHighI16x8S(Node * node)1768 void InstructionSelector::VisitI32x4ExtMulHighI16x8S(Node* node) {
1769   VisitExtMul(this, kArm64Smull2, node, 32);
1770 }
1771 
VisitI32x4ExtMulLowI16x8U(Node * node)1772 void InstructionSelector::VisitI32x4ExtMulLowI16x8U(Node* node) {
1773   VisitExtMul(this, kArm64Umull, node, 32);
1774 }
1775 
VisitI32x4ExtMulHighI16x8U(Node * node)1776 void InstructionSelector::VisitI32x4ExtMulHighI16x8U(Node* node) {
1777   VisitExtMul(this, kArm64Umull2, node, 32);
1778 }
1779 
VisitI64x2ExtMulLowI32x4S(Node * node)1780 void InstructionSelector::VisitI64x2ExtMulLowI32x4S(Node* node) {
1781   VisitExtMul(this, kArm64Smull, node, 64);
1782 }
1783 
VisitI64x2ExtMulHighI32x4S(Node * node)1784 void InstructionSelector::VisitI64x2ExtMulHighI32x4S(Node* node) {
1785   VisitExtMul(this, kArm64Smull2, node, 64);
1786 }
1787 
VisitI64x2ExtMulLowI32x4U(Node * node)1788 void InstructionSelector::VisitI64x2ExtMulLowI32x4U(Node* node) {
1789   VisitExtMul(this, kArm64Umull, node, 64);
1790 }
1791 
VisitI64x2ExtMulHighI32x4U(Node * node)1792 void InstructionSelector::VisitI64x2ExtMulHighI32x4U(Node* node) {
1793   VisitExtMul(this, kArm64Umull2, node, 64);
1794 }
1795 
1796 namespace {
VisitExtAddPairwise(InstructionSelector * selector,ArchOpcode opcode,Node * node,int dst_lane_size)1797 void VisitExtAddPairwise(InstructionSelector* selector, ArchOpcode opcode,
1798                          Node* node, int dst_lane_size) {
1799   InstructionCode code = opcode;
1800   code |= LaneSizeField::encode(dst_lane_size);
1801   VisitRR(selector, code, node);
1802 }
1803 }  // namespace
1804 
VisitI32x4ExtAddPairwiseI16x8S(Node * node)1805 void InstructionSelector::VisitI32x4ExtAddPairwiseI16x8S(Node* node) {
1806   VisitExtAddPairwise(this, kArm64Saddlp, node, 32);
1807 }
1808 
VisitI32x4ExtAddPairwiseI16x8U(Node * node)1809 void InstructionSelector::VisitI32x4ExtAddPairwiseI16x8U(Node* node) {
1810   VisitExtAddPairwise(this, kArm64Uaddlp, node, 32);
1811 }
1812 
VisitI16x8ExtAddPairwiseI8x16S(Node * node)1813 void InstructionSelector::VisitI16x8ExtAddPairwiseI8x16S(Node* node) {
1814   VisitExtAddPairwise(this, kArm64Saddlp, node, 16);
1815 }
1816 
VisitI16x8ExtAddPairwiseI8x16U(Node * node)1817 void InstructionSelector::VisitI16x8ExtAddPairwiseI8x16U(Node* node) {
1818   VisitExtAddPairwise(this, kArm64Uaddlp, node, 16);
1819 }
1820 
VisitInt32MulHigh(Node * node)1821 void InstructionSelector::VisitInt32MulHigh(Node* node) {
1822   Arm64OperandGenerator g(this);
1823   InstructionOperand const smull_operand = g.TempRegister();
1824   Emit(kArm64Smull, smull_operand, g.UseRegister(node->InputAt(0)),
1825        g.UseRegister(node->InputAt(1)));
1826   Emit(kArm64Asr, g.DefineAsRegister(node), smull_operand, g.TempImmediate(32));
1827 }
1828 
VisitUint32MulHigh(Node * node)1829 void InstructionSelector::VisitUint32MulHigh(Node* node) {
1830   Arm64OperandGenerator g(this);
1831   InstructionOperand const smull_operand = g.TempRegister();
1832   Emit(kArm64Umull, smull_operand, g.UseRegister(node->InputAt(0)),
1833        g.UseRegister(node->InputAt(1)));
1834   Emit(kArm64Lsr, g.DefineAsRegister(node), smull_operand, g.TempImmediate(32));
1835 }
1836 
VisitTruncateFloat32ToInt32(Node * node)1837 void InstructionSelector::VisitTruncateFloat32ToInt32(Node* node) {
1838   Arm64OperandGenerator g(this);
1839 
1840   InstructionCode opcode = kArm64Float32ToInt32;
1841   TruncateKind kind = OpParameter<TruncateKind>(node->op());
1842   opcode |= MiscField::encode(kind == TruncateKind::kSetOverflowToMin);
1843 
1844   Emit(opcode, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0)));
1845 }
1846 
VisitTruncateFloat32ToUint32(Node * node)1847 void InstructionSelector::VisitTruncateFloat32ToUint32(Node* node) {
1848   Arm64OperandGenerator g(this);
1849 
1850   InstructionCode opcode = kArm64Float32ToUint32;
1851   TruncateKind kind = OpParameter<TruncateKind>(node->op());
1852   if (kind == TruncateKind::kSetOverflowToMin) {
1853     opcode |= MiscField::encode(true);
1854   }
1855 
1856   Emit(opcode, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0)));
1857 }
1858 
VisitTryTruncateFloat32ToInt64(Node * node)1859 void InstructionSelector::VisitTryTruncateFloat32ToInt64(Node* node) {
1860   Arm64OperandGenerator g(this);
1861 
1862   InstructionOperand inputs[] = {g.UseRegister(node->InputAt(0))};
1863   InstructionOperand outputs[2];
1864   size_t output_count = 0;
1865   outputs[output_count++] = g.DefineAsRegister(node);
1866 
1867   Node* success_output = NodeProperties::FindProjection(node, 1);
1868   if (success_output) {
1869     outputs[output_count++] = g.DefineAsRegister(success_output);
1870   }
1871 
1872   Emit(kArm64Float32ToInt64, output_count, outputs, 1, inputs);
1873 }
1874 
VisitTruncateFloat64ToInt64(Node * node)1875 void InstructionSelector::VisitTruncateFloat64ToInt64(Node* node) {
1876   Arm64OperandGenerator g(this);
1877 
1878   InstructionCode opcode = kArm64Float64ToInt64;
1879   TruncateKind kind = OpParameter<TruncateKind>(node->op());
1880   if (kind == TruncateKind::kSetOverflowToMin) {
1881     opcode |= MiscField::encode(true);
1882   }
1883 
1884   Emit(opcode, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0)));
1885 }
1886 
VisitTryTruncateFloat64ToInt64(Node * node)1887 void InstructionSelector::VisitTryTruncateFloat64ToInt64(Node* node) {
1888   Arm64OperandGenerator g(this);
1889 
1890   InstructionOperand inputs[] = {g.UseRegister(node->InputAt(0))};
1891   InstructionOperand outputs[2];
1892   size_t output_count = 0;
1893   outputs[output_count++] = g.DefineAsRegister(node);
1894 
1895   Node* success_output = NodeProperties::FindProjection(node, 1);
1896   if (success_output) {
1897     outputs[output_count++] = g.DefineAsRegister(success_output);
1898   }
1899 
1900   Emit(kArm64Float64ToInt64, output_count, outputs, 1, inputs);
1901 }
1902 
VisitTryTruncateFloat32ToUint64(Node * node)1903 void InstructionSelector::VisitTryTruncateFloat32ToUint64(Node* node) {
1904   Arm64OperandGenerator g(this);
1905 
1906   InstructionOperand inputs[] = {g.UseRegister(node->InputAt(0))};
1907   InstructionOperand outputs[2];
1908   size_t output_count = 0;
1909   outputs[output_count++] = g.DefineAsRegister(node);
1910 
1911   Node* success_output = NodeProperties::FindProjection(node, 1);
1912   if (success_output) {
1913     outputs[output_count++] = g.DefineAsRegister(success_output);
1914   }
1915 
1916   Emit(kArm64Float32ToUint64, output_count, outputs, 1, inputs);
1917 }
1918 
VisitTryTruncateFloat64ToUint64(Node * node)1919 void InstructionSelector::VisitTryTruncateFloat64ToUint64(Node* node) {
1920   Arm64OperandGenerator g(this);
1921 
1922   InstructionOperand inputs[] = {g.UseRegister(node->InputAt(0))};
1923   InstructionOperand outputs[2];
1924   size_t output_count = 0;
1925   outputs[output_count++] = g.DefineAsRegister(node);
1926 
1927   Node* success_output = NodeProperties::FindProjection(node, 1);
1928   if (success_output) {
1929     outputs[output_count++] = g.DefineAsRegister(success_output);
1930   }
1931 
1932   Emit(kArm64Float64ToUint64, output_count, outputs, 1, inputs);
1933 }
1934 
VisitBitcastWord32ToWord64(Node * node)1935 void InstructionSelector::VisitBitcastWord32ToWord64(Node* node) {
1936   DCHECK(SmiValuesAre31Bits());
1937   DCHECK(COMPRESS_POINTERS_BOOL);
1938   EmitIdentity(node);
1939 }
1940 
VisitChangeInt32ToInt64(Node * node)1941 void InstructionSelector::VisitChangeInt32ToInt64(Node* node) {
1942   Node* value = node->InputAt(0);
1943   if ((value->opcode() == IrOpcode::kLoad ||
1944        value->opcode() == IrOpcode::kLoadImmutable) &&
1945       CanCover(node, value)) {
1946     // Generate sign-extending load.
1947     LoadRepresentation load_rep = LoadRepresentationOf(value->op());
1948     MachineRepresentation rep = load_rep.representation();
1949     InstructionCode opcode = kArchNop;
1950     ImmediateMode immediate_mode = kNoImmediate;
1951     switch (rep) {
1952       case MachineRepresentation::kBit:  // Fall through.
1953       case MachineRepresentation::kWord8:
1954         opcode = load_rep.IsSigned() ? kArm64Ldrsb : kArm64Ldrb;
1955         immediate_mode = kLoadStoreImm8;
1956         break;
1957       case MachineRepresentation::kWord16:
1958         opcode = load_rep.IsSigned() ? kArm64Ldrsh : kArm64Ldrh;
1959         immediate_mode = kLoadStoreImm16;
1960         break;
1961       case MachineRepresentation::kWord32:
1962         opcode = kArm64Ldrsw;
1963         immediate_mode = kLoadStoreImm32;
1964         break;
1965       default:
1966         UNREACHABLE();
1967     }
1968     EmitLoad(this, value, opcode, immediate_mode, rep, node);
1969     return;
1970   }
1971 
1972   if (value->opcode() == IrOpcode::kWord32Sar && CanCover(node, value)) {
1973     Int32BinopMatcher m(value);
1974     if (m.right().HasResolvedValue()) {
1975       Arm64OperandGenerator g(this);
1976       // Mask the shift amount, to keep the same semantics as Word32Sar.
1977       int right = m.right().ResolvedValue() & 0x1F;
1978       Emit(kArm64Sbfx, g.DefineAsRegister(node), g.UseRegister(m.left().node()),
1979            g.TempImmediate(right), g.TempImmediate(32 - right));
1980       return;
1981     }
1982   }
1983 
1984   VisitRR(this, kArm64Sxtw, node);
1985 }
1986 
ZeroExtendsWord32ToWord64NoPhis(Node * node)1987 bool InstructionSelector::ZeroExtendsWord32ToWord64NoPhis(Node* node) {
1988   DCHECK_NE(node->opcode(), IrOpcode::kPhi);
1989   switch (node->opcode()) {
1990     case IrOpcode::kWord32And:
1991     case IrOpcode::kWord32Or:
1992     case IrOpcode::kWord32Xor:
1993     case IrOpcode::kWord32Shl:
1994     case IrOpcode::kWord32Shr:
1995     case IrOpcode::kWord32Sar:
1996     case IrOpcode::kWord32Ror:
1997     case IrOpcode::kWord32Equal:
1998     case IrOpcode::kInt32Add:
1999     case IrOpcode::kInt32AddWithOverflow:
2000     case IrOpcode::kInt32Sub:
2001     case IrOpcode::kInt32SubWithOverflow:
2002     case IrOpcode::kInt32Mul:
2003     case IrOpcode::kInt32MulHigh:
2004     case IrOpcode::kInt32Div:
2005     case IrOpcode::kInt32Mod:
2006     case IrOpcode::kInt32LessThan:
2007     case IrOpcode::kInt32LessThanOrEqual:
2008     case IrOpcode::kUint32Div:
2009     case IrOpcode::kUint32LessThan:
2010     case IrOpcode::kUint32LessThanOrEqual:
2011     case IrOpcode::kUint32Mod:
2012     case IrOpcode::kUint32MulHigh: {
2013       // 32-bit operations will write their result in a W register (implicitly
2014       // clearing the top 32-bit of the corresponding X register) so the
2015       // zero-extension is a no-op.
2016       return true;
2017     }
2018     case IrOpcode::kLoad:
2019     case IrOpcode::kLoadImmutable: {
2020       // As for the operations above, a 32-bit load will implicitly clear the
2021       // top 32 bits of the destination register.
2022       LoadRepresentation load_rep = LoadRepresentationOf(node->op());
2023       switch (load_rep.representation()) {
2024         case MachineRepresentation::kWord8:
2025         case MachineRepresentation::kWord16:
2026         case MachineRepresentation::kWord32:
2027           return true;
2028         default:
2029           return false;
2030       }
2031     }
2032     default:
2033       return false;
2034   }
2035 }
2036 
VisitChangeUint32ToUint64(Node * node)2037 void InstructionSelector::VisitChangeUint32ToUint64(Node* node) {
2038   Arm64OperandGenerator g(this);
2039   Node* value = node->InputAt(0);
2040   if (ZeroExtendsWord32ToWord64(value)) {
2041     return EmitIdentity(node);
2042   }
2043   Emit(kArm64Mov32, g.DefineAsRegister(node), g.UseRegister(value));
2044 }
2045 
VisitTruncateInt64ToInt32(Node * node)2046 void InstructionSelector::VisitTruncateInt64ToInt32(Node* node) {
2047   Arm64OperandGenerator g(this);
2048   // The top 32 bits in the 64-bit register will be undefined, and
2049   // must not be used by a dependent node.
2050   EmitIdentity(node);
2051 }
2052 
VisitFloat64Mod(Node * node)2053 void InstructionSelector::VisitFloat64Mod(Node* node) {
2054   Arm64OperandGenerator g(this);
2055   Emit(kArm64Float64Mod, g.DefineAsFixed(node, d0),
2056        g.UseFixed(node->InputAt(0), d0), g.UseFixed(node->InputAt(1), d1))
2057       ->MarkAsCall();
2058 }
2059 
VisitFloat64Ieee754Binop(Node * node,InstructionCode opcode)2060 void InstructionSelector::VisitFloat64Ieee754Binop(Node* node,
2061                                                    InstructionCode opcode) {
2062   Arm64OperandGenerator g(this);
2063   Emit(opcode, g.DefineAsFixed(node, d0), g.UseFixed(node->InputAt(0), d0),
2064        g.UseFixed(node->InputAt(1), d1))
2065       ->MarkAsCall();
2066 }
2067 
VisitFloat64Ieee754Unop(Node * node,InstructionCode opcode)2068 void InstructionSelector::VisitFloat64Ieee754Unop(Node* node,
2069                                                   InstructionCode opcode) {
2070   Arm64OperandGenerator g(this);
2071   Emit(opcode, g.DefineAsFixed(node, d0), g.UseFixed(node->InputAt(0), d0))
2072       ->MarkAsCall();
2073 }
2074 
EmitPrepareArguments(ZoneVector<PushParameter> * arguments,const CallDescriptor * call_descriptor,Node * node)2075 void InstructionSelector::EmitPrepareArguments(
2076     ZoneVector<PushParameter>* arguments, const CallDescriptor* call_descriptor,
2077     Node* node) {
2078   Arm64OperandGenerator g(this);
2079 
2080   // `arguments` includes alignment "holes". This means that slots bigger than
2081   // kSystemPointerSize, e.g. Simd128, will span across multiple arguments.
2082   int claim_count = static_cast<int>(arguments->size());
2083   bool needs_padding = claim_count % 2 != 0;
2084   int slot = claim_count - 1;
2085   claim_count = RoundUp(claim_count, 2);
2086   // Bump the stack pointer.
2087   if (claim_count > 0) {
2088     // TODO(titzer): claim and poke probably take small immediates.
2089     // TODO(titzer): it would be better to bump the sp here only
2090     //               and emit paired stores with increment for non c frames.
2091     Emit(kArm64Claim, g.NoOutput(), g.TempImmediate(claim_count));
2092 
2093     if (needs_padding) {
2094       Emit(kArm64Poke, g.NoOutput(), g.UseImmediate(0),
2095            g.TempImmediate(claim_count - 1));
2096     }
2097   }
2098 
2099   // Poke the arguments into the stack.
2100   while (slot >= 0) {
2101     PushParameter input0 = (*arguments)[slot];
2102     // Skip holes in the param array. These represent both extra slots for
2103     // multi-slot values and padding slots for alignment.
2104     if (input0.node == nullptr) {
2105       slot--;
2106       continue;
2107     }
2108     PushParameter input1 = slot > 0 ? (*arguments)[slot - 1] : PushParameter();
2109     // Emit a poke-pair if consecutive parameters have the same type.
2110     // TODO(arm): Support consecutive Simd128 parameters.
2111     if (input1.node != nullptr &&
2112         input0.location.GetType() == input1.location.GetType()) {
2113       Emit(kArm64PokePair, g.NoOutput(), g.UseRegister(input0.node),
2114            g.UseRegister(input1.node), g.TempImmediate(slot));
2115       slot -= 2;
2116     } else {
2117       Emit(kArm64Poke, g.NoOutput(), g.UseRegister(input0.node),
2118            g.TempImmediate(slot));
2119       slot--;
2120     }
2121   }
2122 }
2123 
EmitPrepareResults(ZoneVector<PushParameter> * results,const CallDescriptor * call_descriptor,Node * node)2124 void InstructionSelector::EmitPrepareResults(
2125     ZoneVector<PushParameter>* results, const CallDescriptor* call_descriptor,
2126     Node* node) {
2127   Arm64OperandGenerator g(this);
2128 
2129   for (PushParameter output : *results) {
2130     if (!output.location.IsCallerFrameSlot()) continue;
2131     // Skip any alignment holes in nodes.
2132     if (output.node != nullptr) {
2133       DCHECK(!call_descriptor->IsCFunctionCall());
2134 
2135       if (output.location.GetType() == MachineType::Float32()) {
2136         MarkAsFloat32(output.node);
2137       } else if (output.location.GetType() == MachineType::Float64()) {
2138         MarkAsFloat64(output.node);
2139       } else if (output.location.GetType() == MachineType::Simd128()) {
2140         MarkAsSimd128(output.node);
2141       }
2142 
2143       int offset = call_descriptor->GetOffsetToReturns();
2144       int reverse_slot = -output.location.GetLocation() - offset;
2145       Emit(kArm64Peek, g.DefineAsRegister(output.node),
2146            g.UseImmediate(reverse_slot));
2147     }
2148   }
2149 }
2150 
IsTailCallAddressImmediate()2151 bool InstructionSelector::IsTailCallAddressImmediate() { return false; }
2152 
2153 namespace {
2154 
2155 // Shared routine for multiple compare operations.
VisitCompare(InstructionSelector * selector,InstructionCode opcode,InstructionOperand left,InstructionOperand right,FlagsContinuation * cont)2156 void VisitCompare(InstructionSelector* selector, InstructionCode opcode,
2157                   InstructionOperand left, InstructionOperand right,
2158                   FlagsContinuation* cont) {
2159   if (cont->IsSelect()) {
2160     Arm64OperandGenerator g(selector);
2161     InstructionOperand inputs[] = {left, right,
2162                                    g.UseRegister(cont->true_value()),
2163                                    g.UseRegister(cont->false_value())};
2164     selector->EmitWithContinuation(opcode, 0, nullptr, 4, inputs, cont);
2165   } else {
2166     selector->EmitWithContinuation(opcode, left, right, cont);
2167   }
2168 }
2169 
2170 // This function checks whether we can convert:
2171 // ((a <op> b) cmp 0), b.<cond>
2172 // to:
2173 // (a <ops> b), b.<cond'>
2174 // where <ops> is the flag setting version of <op>.
2175 // We only generate conditions <cond'> that are a combination of the N
2176 // and Z flags. This avoids the need to make this function dependent on
2177 // the flag-setting operation.
CanUseFlagSettingBinop(FlagsCondition cond)2178 bool CanUseFlagSettingBinop(FlagsCondition cond) {
2179   switch (cond) {
2180     case kEqual:
2181     case kNotEqual:
2182     case kSignedLessThan:
2183     case kSignedGreaterThanOrEqual:
2184     case kUnsignedLessThanOrEqual:  // x <= 0 -> x == 0
2185     case kUnsignedGreaterThan:      // x > 0 -> x != 0
2186       return true;
2187     default:
2188       return false;
2189   }
2190 }
2191 
2192 // Map <cond> to <cond'> so that the following transformation is possible:
2193 // ((a <op> b) cmp 0), b.<cond>
2194 // to:
2195 // (a <ops> b), b.<cond'>
2196 // where <ops> is the flag setting version of <op>.
MapForFlagSettingBinop(FlagsCondition cond)2197 FlagsCondition MapForFlagSettingBinop(FlagsCondition cond) {
2198   DCHECK(CanUseFlagSettingBinop(cond));
2199   switch (cond) {
2200     case kEqual:
2201     case kNotEqual:
2202       return cond;
2203     case kSignedLessThan:
2204       return kNegative;
2205     case kSignedGreaterThanOrEqual:
2206       return kPositiveOrZero;
2207     case kUnsignedLessThanOrEqual:  // x <= 0 -> x == 0
2208       return kEqual;
2209     case kUnsignedGreaterThan:  // x > 0 -> x != 0
2210       return kNotEqual;
2211     default:
2212       UNREACHABLE();
2213   }
2214 }
2215 
2216 // This function checks if we can perform the transformation:
2217 // ((a <op> b) cmp 0), b.<cond>
2218 // to:
2219 // (a <ops> b), b.<cond'>
2220 // where <ops> is the flag setting version of <op>, and if so,
2221 // updates {node}, {opcode} and {cont} accordingly.
MaybeReplaceCmpZeroWithFlagSettingBinop(InstructionSelector * selector,Node ** node,Node * binop,ArchOpcode * opcode,FlagsCondition cond,FlagsContinuation * cont,ImmediateMode * immediate_mode)2222 void MaybeReplaceCmpZeroWithFlagSettingBinop(InstructionSelector* selector,
2223                                              Node** node, Node* binop,
2224                                              ArchOpcode* opcode,
2225                                              FlagsCondition cond,
2226                                              FlagsContinuation* cont,
2227                                              ImmediateMode* immediate_mode) {
2228   ArchOpcode binop_opcode;
2229   ArchOpcode no_output_opcode;
2230   ImmediateMode binop_immediate_mode;
2231   switch (binop->opcode()) {
2232     case IrOpcode::kInt32Add:
2233       binop_opcode = kArm64Add32;
2234       no_output_opcode = kArm64Cmn32;
2235       binop_immediate_mode = kArithmeticImm;
2236       break;
2237     case IrOpcode::kWord32And:
2238       binop_opcode = kArm64And32;
2239       no_output_opcode = kArm64Tst32;
2240       binop_immediate_mode = kLogical32Imm;
2241       break;
2242     default:
2243       UNREACHABLE();
2244   }
2245   if (selector->CanCover(*node, binop)) {
2246     // The comparison is the only user of the add or and, so we can generate
2247     // a cmn or tst instead.
2248     cont->Overwrite(MapForFlagSettingBinop(cond));
2249     *opcode = no_output_opcode;
2250     *node = binop;
2251     *immediate_mode = binop_immediate_mode;
2252   } else if (selector->IsOnlyUserOfNodeInSameBlock(*node, binop)) {
2253     // We can also handle the case where the add and the compare are in the
2254     // same basic block, and the compare is the only use of add in this basic
2255     // block (the add has users in other basic blocks).
2256     cont->Overwrite(MapForFlagSettingBinop(cond));
2257     *opcode = binop_opcode;
2258     *node = binop;
2259     *immediate_mode = binop_immediate_mode;
2260   }
2261 }
2262 
2263 // Map {cond} to kEqual or kNotEqual, so that we can select
2264 // either TBZ or TBNZ when generating code for:
2265 // (x cmp 0), b.{cond}
MapForTbz(FlagsCondition cond)2266 FlagsCondition MapForTbz(FlagsCondition cond) {
2267   switch (cond) {
2268     case kSignedLessThan:  // generate TBNZ
2269       return kNotEqual;
2270     case kSignedGreaterThanOrEqual:  // generate TBZ
2271       return kEqual;
2272     default:
2273       UNREACHABLE();
2274   }
2275 }
2276 
2277 // Map {cond} to kEqual or kNotEqual, so that we can select
2278 // either CBZ or CBNZ when generating code for:
2279 // (x cmp 0), b.{cond}
MapForCbz(FlagsCondition cond)2280 FlagsCondition MapForCbz(FlagsCondition cond) {
2281   switch (cond) {
2282     case kEqual:     // generate CBZ
2283     case kNotEqual:  // generate CBNZ
2284       return cond;
2285     case kUnsignedLessThanOrEqual:  // generate CBZ
2286       return kEqual;
2287     case kUnsignedGreaterThan:  // generate CBNZ
2288       return kNotEqual;
2289     default:
2290       UNREACHABLE();
2291   }
2292 }
2293 
EmitBranchOrDeoptimize(InstructionSelector * selector,InstructionCode opcode,InstructionOperand value,FlagsContinuation * cont)2294 void EmitBranchOrDeoptimize(InstructionSelector* selector,
2295                             InstructionCode opcode, InstructionOperand value,
2296                             FlagsContinuation* cont) {
2297   DCHECK(cont->IsBranch() || cont->IsDeoptimize());
2298   selector->EmitWithContinuation(opcode, value, cont);
2299 }
2300 
2301 template <int N>
2302 struct CbzOrTbzMatchTrait {};
2303 
2304 template <>
2305 struct CbzOrTbzMatchTrait<32> {
2306   using IntegralType = uint32_t;
2307   using BinopMatcher = Int32BinopMatcher;
2308   static constexpr IrOpcode::Value kAndOpcode = IrOpcode::kWord32And;
2309   static constexpr ArchOpcode kTestAndBranchOpcode = kArm64TestAndBranch32;
2310   static constexpr ArchOpcode kCompareAndBranchOpcode =
2311       kArm64CompareAndBranch32;
2312   static constexpr unsigned kSignBit = kWSignBit;
2313 };
2314 
2315 template <>
2316 struct CbzOrTbzMatchTrait<64> {
2317   using IntegralType = uint64_t;
2318   using BinopMatcher = Int64BinopMatcher;
2319   static constexpr IrOpcode::Value kAndOpcode = IrOpcode::kWord64And;
2320   static constexpr ArchOpcode kTestAndBranchOpcode = kArm64TestAndBranch;
2321   static constexpr ArchOpcode kCompareAndBranchOpcode = kArm64CompareAndBranch;
2322   static constexpr unsigned kSignBit = kXSignBit;
2323 };
2324 
2325 // Try to emit TBZ, TBNZ, CBZ or CBNZ for certain comparisons of {node}
2326 // against {value}, depending on the condition.
2327 template <int N>
TryEmitCbzOrTbz(InstructionSelector * selector,Node * node,typename CbzOrTbzMatchTrait<N>::IntegralType value,Node * user,FlagsCondition cond,FlagsContinuation * cont)2328 bool TryEmitCbzOrTbz(InstructionSelector* selector, Node* node,
2329                      typename CbzOrTbzMatchTrait<N>::IntegralType value,
2330                      Node* user, FlagsCondition cond, FlagsContinuation* cont) {
2331   // Only handle branches and deoptimisations.
2332   if (!cont->IsBranch() && !cont->IsDeoptimize()) return false;
2333 
2334   switch (cond) {
2335     case kSignedLessThan:
2336     case kSignedGreaterThanOrEqual: {
2337       // Here we handle sign tests, aka. comparisons with zero.
2338       if (value != 0) return false;
2339       // We don't generate TBZ/TBNZ for deoptimisations, as they have a
2340       // shorter range than conditional branches and generating them for
2341       // deoptimisations results in more veneers.
2342       if (cont->IsDeoptimize()) return false;
2343       Arm64OperandGenerator g(selector);
2344       cont->Overwrite(MapForTbz(cond));
2345 
2346       if (N == 32) {
2347         Int32Matcher m(node);
2348         if (m.IsFloat64ExtractHighWord32() && selector->CanCover(user, node)) {
2349           // SignedLessThan(Float64ExtractHighWord32(x), 0) and
2350           // SignedGreaterThanOrEqual(Float64ExtractHighWord32(x), 0)
2351           // essentially check the sign bit of a 64-bit floating point value.
2352           InstructionOperand temp = g.TempRegister();
2353           selector->Emit(kArm64U64MoveFloat64, temp,
2354                          g.UseRegister(node->InputAt(0)));
2355           selector->EmitWithContinuation(kArm64TestAndBranch, temp,
2356                                          g.TempImmediate(kDSignBit), cont);
2357           return true;
2358         }
2359       }
2360 
2361       selector->EmitWithContinuation(
2362           CbzOrTbzMatchTrait<N>::kTestAndBranchOpcode, g.UseRegister(node),
2363           g.TempImmediate(CbzOrTbzMatchTrait<N>::kSignBit), cont);
2364       return true;
2365     }
2366     case kEqual:
2367     case kNotEqual: {
2368       if (node->opcode() == CbzOrTbzMatchTrait<N>::kAndOpcode) {
2369         // Emit a tbz/tbnz if we are comparing with a single-bit mask:
2370         //   Branch(WordEqual(WordAnd(x, 1 << N), 1 << N), true, false)
2371         typename CbzOrTbzMatchTrait<N>::BinopMatcher m_and(node);
2372         if (cont->IsBranch() && base::bits::IsPowerOfTwo(value) &&
2373             m_and.right().Is(value) && selector->CanCover(user, node)) {
2374           Arm64OperandGenerator g(selector);
2375           // In the code generator, Equal refers to a bit being cleared. We want
2376           // the opposite here so negate the condition.
2377           cont->Negate();
2378           selector->EmitWithContinuation(
2379               CbzOrTbzMatchTrait<N>::kTestAndBranchOpcode,
2380               g.UseRegister(m_and.left().node()),
2381               g.TempImmediate(base::bits::CountTrailingZeros(value)), cont);
2382           return true;
2383         }
2384       }
2385       V8_FALLTHROUGH;
2386     }
2387     case kUnsignedLessThanOrEqual:
2388     case kUnsignedGreaterThan: {
2389       if (value != 0) return false;
2390       Arm64OperandGenerator g(selector);
2391       cont->Overwrite(MapForCbz(cond));
2392       EmitBranchOrDeoptimize(selector,
2393                              CbzOrTbzMatchTrait<N>::kCompareAndBranchOpcode,
2394                              g.UseRegister(node), cont);
2395       return true;
2396     }
2397     default:
2398       return false;
2399   }
2400 }
2401 
2402 // Shared routine for multiple word compare operations.
VisitWordCompare(InstructionSelector * selector,Node * node,InstructionCode opcode,FlagsContinuation * cont,ImmediateMode immediate_mode)2403 void VisitWordCompare(InstructionSelector* selector, Node* node,
2404                       InstructionCode opcode, FlagsContinuation* cont,
2405                       ImmediateMode immediate_mode) {
2406   Arm64OperandGenerator g(selector);
2407 
2408   Node* left = node->InputAt(0);
2409   Node* right = node->InputAt(1);
2410 
2411   // If one of the two inputs is an immediate, make sure it's on the right.
2412   if (!g.CanBeImmediate(right, immediate_mode) &&
2413       g.CanBeImmediate(left, immediate_mode)) {
2414     cont->Commute();
2415     std::swap(left, right);
2416   }
2417 
2418   if (opcode == kArm64Cmp) {
2419     Int64Matcher m(right);
2420     if (m.HasResolvedValue()) {
2421       if (TryEmitCbzOrTbz<64>(selector, left, m.ResolvedValue(), node,
2422                               cont->condition(), cont)) {
2423         return;
2424       }
2425     }
2426   }
2427 
2428   VisitCompare(selector, opcode, g.UseRegister(left),
2429                g.UseOperand(right, immediate_mode), cont);
2430 }
2431 
VisitWord32Compare(InstructionSelector * selector,Node * node,FlagsContinuation * cont)2432 void VisitWord32Compare(InstructionSelector* selector, Node* node,
2433                         FlagsContinuation* cont) {
2434   Int32BinopMatcher m(node);
2435   FlagsCondition cond = cont->condition();
2436   if (m.right().HasResolvedValue()) {
2437     if (TryEmitCbzOrTbz<32>(selector, m.left().node(),
2438                             m.right().ResolvedValue(), node, cond, cont)) {
2439       return;
2440     }
2441   } else if (m.left().HasResolvedValue()) {
2442     FlagsCondition commuted_cond = CommuteFlagsCondition(cond);
2443     if (TryEmitCbzOrTbz<32>(selector, m.right().node(),
2444                             m.left().ResolvedValue(), node, commuted_cond,
2445                             cont)) {
2446       return;
2447     }
2448   }
2449   ArchOpcode opcode = kArm64Cmp32;
2450   ImmediateMode immediate_mode = kArithmeticImm;
2451   if (m.right().Is(0) && (m.left().IsInt32Add() || m.left().IsWord32And())) {
2452     // Emit flag setting add/and instructions for comparisons against zero.
2453     if (CanUseFlagSettingBinop(cond)) {
2454       Node* binop = m.left().node();
2455       MaybeReplaceCmpZeroWithFlagSettingBinop(selector, &node, binop, &opcode,
2456                                               cond, cont, &immediate_mode);
2457     }
2458   } else if (m.left().Is(0) &&
2459              (m.right().IsInt32Add() || m.right().IsWord32And())) {
2460     // Same as above, but we need to commute the condition before we
2461     // continue with the rest of the checks.
2462     FlagsCondition commuted_cond = CommuteFlagsCondition(cond);
2463     if (CanUseFlagSettingBinop(commuted_cond)) {
2464       Node* binop = m.right().node();
2465       MaybeReplaceCmpZeroWithFlagSettingBinop(selector, &node, binop, &opcode,
2466                                               commuted_cond, cont,
2467                                               &immediate_mode);
2468     }
2469   } else if (m.right().IsInt32Sub() && (cond == kEqual || cond == kNotEqual)) {
2470     // Select negated compare for comparisons with negated right input.
2471     // Only do this for kEqual and kNotEqual, which do not depend on the
2472     // C and V flags, as those flags will be different with CMN when the
2473     // right-hand side of the original subtraction is INT_MIN.
2474     Node* sub = m.right().node();
2475     Int32BinopMatcher msub(sub);
2476     if (msub.left().Is(0)) {
2477       bool can_cover = selector->CanCover(node, sub);
2478       node->ReplaceInput(1, msub.right().node());
2479       // Even if the comparison node covers the subtraction, after the input
2480       // replacement above, the node still won't cover the input to the
2481       // subtraction; the subtraction still uses it.
2482       // In order to get shifted operations to work, we must remove the rhs
2483       // input to the subtraction, as TryMatchAnyShift requires this node to
2484       // cover the input shift. We do this by setting it to the lhs input,
2485       // as we know it's zero, and the result of the subtraction isn't used by
2486       // any other node.
2487       if (can_cover) sub->ReplaceInput(1, msub.left().node());
2488       opcode = kArm64Cmn32;
2489     }
2490   }
2491   VisitBinop<Int32BinopMatcher>(selector, node, opcode, immediate_mode, cont);
2492 }
2493 
VisitWordTest(InstructionSelector * selector,Node * node,InstructionCode opcode,FlagsContinuation * cont)2494 void VisitWordTest(InstructionSelector* selector, Node* node,
2495                    InstructionCode opcode, FlagsContinuation* cont) {
2496   Arm64OperandGenerator g(selector);
2497   VisitCompare(selector, opcode, g.UseRegister(node), g.UseRegister(node),
2498                cont);
2499 }
2500 
VisitWord32Test(InstructionSelector * selector,Node * node,FlagsContinuation * cont)2501 void VisitWord32Test(InstructionSelector* selector, Node* node,
2502                      FlagsContinuation* cont) {
2503   VisitWordTest(selector, node, kArm64Tst32, cont);
2504 }
2505 
VisitWord64Test(InstructionSelector * selector,Node * node,FlagsContinuation * cont)2506 void VisitWord64Test(InstructionSelector* selector, Node* node,
2507                      FlagsContinuation* cont) {
2508   VisitWordTest(selector, node, kArm64Tst, cont);
2509 }
2510 
2511 template <typename Matcher>
2512 struct TestAndBranchMatcher {
TestAndBranchMatcherv8::internal::compiler::__anon1f5f66280711::TestAndBranchMatcher2513   TestAndBranchMatcher(Node* node, FlagsContinuation* cont)
2514       : matches_(false), cont_(cont), matcher_(node) {
2515     Initialize();
2516   }
Matchesv8::internal::compiler::__anon1f5f66280711::TestAndBranchMatcher2517   bool Matches() const { return matches_; }
2518 
bitv8::internal::compiler::__anon1f5f66280711::TestAndBranchMatcher2519   unsigned bit() const {
2520     DCHECK(Matches());
2521     return base::bits::CountTrailingZeros(matcher_.right().ResolvedValue());
2522   }
2523 
inputv8::internal::compiler::__anon1f5f66280711::TestAndBranchMatcher2524   Node* input() const {
2525     DCHECK(Matches());
2526     return matcher_.left().node();
2527   }
2528 
2529  private:
2530   bool matches_;
2531   FlagsContinuation* cont_;
2532   Matcher matcher_;
2533 
Initializev8::internal::compiler::__anon1f5f66280711::TestAndBranchMatcher2534   void Initialize() {
2535     if (cont_->IsBranch() && matcher_.right().HasResolvedValue() &&
2536         base::bits::IsPowerOfTwo(matcher_.right().ResolvedValue())) {
2537       // If the mask has only one bit set, we can use tbz/tbnz.
2538       DCHECK((cont_->condition() == kEqual) ||
2539              (cont_->condition() == kNotEqual));
2540       matches_ = true;
2541     } else {
2542       matches_ = false;
2543     }
2544   }
2545 };
2546 
2547 // Shared routine for multiple float32 compare operations.
VisitFloat32Compare(InstructionSelector * selector,Node * node,FlagsContinuation * cont)2548 void VisitFloat32Compare(InstructionSelector* selector, Node* node,
2549                          FlagsContinuation* cont) {
2550   Arm64OperandGenerator g(selector);
2551   Float32BinopMatcher m(node);
2552   if (m.right().Is(0.0f)) {
2553     VisitCompare(selector, kArm64Float32Cmp, g.UseRegister(m.left().node()),
2554                  g.UseImmediate(m.right().node()), cont);
2555   } else if (m.left().Is(0.0f)) {
2556     cont->Commute();
2557     VisitCompare(selector, kArm64Float32Cmp, g.UseRegister(m.right().node()),
2558                  g.UseImmediate(m.left().node()), cont);
2559   } else {
2560     VisitCompare(selector, kArm64Float32Cmp, g.UseRegister(m.left().node()),
2561                  g.UseRegister(m.right().node()), cont);
2562   }
2563 }
2564 
2565 // Shared routine for multiple float64 compare operations.
VisitFloat64Compare(InstructionSelector * selector,Node * node,FlagsContinuation * cont)2566 void VisitFloat64Compare(InstructionSelector* selector, Node* node,
2567                          FlagsContinuation* cont) {
2568   Arm64OperandGenerator g(selector);
2569   Float64BinopMatcher m(node);
2570   if (m.right().Is(0.0)) {
2571     VisitCompare(selector, kArm64Float64Cmp, g.UseRegister(m.left().node()),
2572                  g.UseImmediate(m.right().node()), cont);
2573   } else if (m.left().Is(0.0)) {
2574     cont->Commute();
2575     VisitCompare(selector, kArm64Float64Cmp, g.UseRegister(m.right().node()),
2576                  g.UseImmediate(m.left().node()), cont);
2577   } else {
2578     VisitCompare(selector, kArm64Float64Cmp, g.UseRegister(m.left().node()),
2579                  g.UseRegister(m.right().node()), cont);
2580   }
2581 }
2582 
VisitAtomicExchange(InstructionSelector * selector,Node * node,ArchOpcode opcode,AtomicWidth width)2583 void VisitAtomicExchange(InstructionSelector* selector, Node* node,
2584                          ArchOpcode opcode, AtomicWidth width) {
2585   Arm64OperandGenerator g(selector);
2586   Node* base = node->InputAt(0);
2587   Node* index = node->InputAt(1);
2588   Node* value = node->InputAt(2);
2589   InstructionOperand inputs[] = {g.UseRegister(base), g.UseRegister(index),
2590                                  g.UseUniqueRegister(value)};
2591   InstructionOperand outputs[] = {g.DefineAsRegister(node)};
2592   InstructionOperand temps[] = {g.TempRegister(), g.TempRegister()};
2593   InstructionCode code = opcode | AddressingModeField::encode(kMode_MRR) |
2594                          AtomicWidthField::encode(width);
2595   selector->Emit(code, arraysize(outputs), outputs, arraysize(inputs), inputs,
2596                  arraysize(temps), temps);
2597 }
2598 
VisitAtomicCompareExchange(InstructionSelector * selector,Node * node,ArchOpcode opcode,AtomicWidth width)2599 void VisitAtomicCompareExchange(InstructionSelector* selector, Node* node,
2600                                 ArchOpcode opcode, AtomicWidth width) {
2601   Arm64OperandGenerator g(selector);
2602   Node* base = node->InputAt(0);
2603   Node* index = node->InputAt(1);
2604   Node* old_value = node->InputAt(2);
2605   Node* new_value = node->InputAt(3);
2606   InstructionOperand inputs[] = {g.UseRegister(base), g.UseRegister(index),
2607                                  g.UseUniqueRegister(old_value),
2608                                  g.UseUniqueRegister(new_value)};
2609   InstructionOperand outputs[] = {g.DefineAsRegister(node)};
2610   InstructionOperand temps[] = {g.TempRegister(), g.TempRegister()};
2611   InstructionCode code = opcode | AddressingModeField::encode(kMode_MRR) |
2612                          AtomicWidthField::encode(width);
2613   selector->Emit(code, arraysize(outputs), outputs, arraysize(inputs), inputs,
2614                  arraysize(temps), temps);
2615 }
2616 
VisitAtomicLoad(InstructionSelector * selector,Node * node,AtomicWidth width)2617 void VisitAtomicLoad(InstructionSelector* selector, Node* node,
2618                      AtomicWidth width) {
2619   Arm64OperandGenerator g(selector);
2620   Node* base = node->InputAt(0);
2621   Node* index = node->InputAt(1);
2622   InstructionOperand inputs[] = {g.UseRegister(base), g.UseRegister(index)};
2623   InstructionOperand outputs[] = {g.DefineAsRegister(node)};
2624   InstructionOperand temps[] = {g.TempRegister()};
2625 
2626   // The memory order is ignored as both acquire and sequentially consistent
2627   // loads can emit LDAR.
2628   // https://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html
2629   AtomicLoadParameters atomic_load_params = AtomicLoadParametersOf(node->op());
2630   LoadRepresentation load_rep = atomic_load_params.representation();
2631   InstructionCode code;
2632   switch (load_rep.representation()) {
2633     case MachineRepresentation::kWord8:
2634       DCHECK_IMPLIES(load_rep.IsSigned(), width == AtomicWidth::kWord32);
2635       code = load_rep.IsSigned() ? kAtomicLoadInt8 : kAtomicLoadUint8;
2636       break;
2637     case MachineRepresentation::kWord16:
2638       DCHECK_IMPLIES(load_rep.IsSigned(), width == AtomicWidth::kWord32);
2639       code = load_rep.IsSigned() ? kAtomicLoadInt16 : kAtomicLoadUint16;
2640       break;
2641     case MachineRepresentation::kWord32:
2642       code = kAtomicLoadWord32;
2643       break;
2644     case MachineRepresentation::kWord64:
2645       code = kArm64Word64AtomicLoadUint64;
2646       break;
2647 #ifdef V8_COMPRESS_POINTERS
2648     case MachineRepresentation::kTaggedSigned:
2649       code = kArm64LdarDecompressTaggedSigned;
2650       break;
2651     case MachineRepresentation::kTaggedPointer:
2652       code = kArm64LdarDecompressTaggedPointer;
2653       break;
2654     case MachineRepresentation::kTagged:
2655       code = kArm64LdarDecompressAnyTagged;
2656       break;
2657 #else
2658     case MachineRepresentation::kTaggedSigned:   // Fall through.
2659     case MachineRepresentation::kTaggedPointer:  // Fall through.
2660     case MachineRepresentation::kTagged:
2661       if (kTaggedSize == 8) {
2662         code = kArm64Word64AtomicLoadUint64;
2663       } else {
2664         code = kAtomicLoadWord32;
2665       }
2666       break;
2667 #endif
2668     case MachineRepresentation::kCompressedPointer:  // Fall through.
2669     case MachineRepresentation::kCompressed:
2670       DCHECK(COMPRESS_POINTERS_BOOL);
2671       code = kAtomicLoadWord32;
2672       break;
2673     default:
2674       UNREACHABLE();
2675   }
2676   code |=
2677       AddressingModeField::encode(kMode_MRR) | AtomicWidthField::encode(width);
2678   selector->Emit(code, arraysize(outputs), outputs, arraysize(inputs), inputs,
2679                  arraysize(temps), temps);
2680 }
2681 
VisitAtomicStore(InstructionSelector * selector,Node * node,AtomicWidth width)2682 void VisitAtomicStore(InstructionSelector* selector, Node* node,
2683                       AtomicWidth width) {
2684   Arm64OperandGenerator g(selector);
2685   Node* base = node->InputAt(0);
2686   Node* index = node->InputAt(1);
2687   Node* value = node->InputAt(2);
2688 
2689   // The memory order is ignored as both release and sequentially consistent
2690   // stores can emit STLR.
2691   // https://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html
2692   AtomicStoreParameters store_params = AtomicStoreParametersOf(node->op());
2693   WriteBarrierKind write_barrier_kind = store_params.write_barrier_kind();
2694   MachineRepresentation rep = store_params.representation();
2695 
2696   if (FLAG_enable_unconditional_write_barriers &&
2697       CanBeTaggedOrCompressedPointer(rep)) {
2698     write_barrier_kind = kFullWriteBarrier;
2699   }
2700 
2701   InstructionOperand inputs[] = {g.UseRegister(base), g.UseRegister(index),
2702                                  g.UseUniqueRegister(value)};
2703   InstructionOperand temps[] = {g.TempRegister()};
2704   InstructionCode code;
2705 
2706   if (write_barrier_kind != kNoWriteBarrier && !FLAG_disable_write_barriers) {
2707     DCHECK(CanBeTaggedOrCompressedPointer(rep));
2708     DCHECK_EQ(AtomicWidthSize(width), kTaggedSize);
2709 
2710     RecordWriteMode record_write_mode =
2711         WriteBarrierKindToRecordWriteMode(write_barrier_kind);
2712     code = kArchAtomicStoreWithWriteBarrier;
2713     code |= MiscField::encode(static_cast<int>(record_write_mode));
2714   } else {
2715     switch (rep) {
2716       case MachineRepresentation::kWord8:
2717         code = kAtomicStoreWord8;
2718         break;
2719       case MachineRepresentation::kWord16:
2720         code = kAtomicStoreWord16;
2721         break;
2722       case MachineRepresentation::kWord32:
2723         code = kAtomicStoreWord32;
2724         break;
2725       case MachineRepresentation::kWord64:
2726         DCHECK_EQ(width, AtomicWidth::kWord64);
2727         code = kArm64Word64AtomicStoreWord64;
2728         break;
2729       case MachineRepresentation::kTaggedSigned:   // Fall through.
2730       case MachineRepresentation::kTaggedPointer:  // Fall through.
2731       case MachineRepresentation::kTagged:
2732         DCHECK_EQ(AtomicWidthSize(width), kTaggedSize);
2733         code = kArm64StlrCompressTagged;
2734         break;
2735       case MachineRepresentation::kCompressedPointer:  // Fall through.
2736       case MachineRepresentation::kCompressed:
2737         CHECK(COMPRESS_POINTERS_BOOL);
2738         DCHECK_EQ(width, AtomicWidth::kWord32);
2739         code = kArm64StlrCompressTagged;
2740         break;
2741       default:
2742         UNREACHABLE();
2743     }
2744     code |= AtomicWidthField::encode(width);
2745   }
2746 
2747   code |= AddressingModeField::encode(kMode_MRR);
2748   selector->Emit(code, 0, nullptr, arraysize(inputs), inputs, arraysize(temps),
2749                  temps);
2750 }
2751 
VisitAtomicBinop(InstructionSelector * selector,Node * node,ArchOpcode opcode,AtomicWidth width)2752 void VisitAtomicBinop(InstructionSelector* selector, Node* node,
2753                       ArchOpcode opcode, AtomicWidth width) {
2754   Arm64OperandGenerator g(selector);
2755   Node* base = node->InputAt(0);
2756   Node* index = node->InputAt(1);
2757   Node* value = node->InputAt(2);
2758   AddressingMode addressing_mode = kMode_MRR;
2759   InstructionOperand inputs[] = {g.UseRegister(base), g.UseRegister(index),
2760                                  g.UseUniqueRegister(value)};
2761   InstructionOperand outputs[] = {g.DefineAsRegister(node)};
2762   InstructionOperand temps[] = {g.TempRegister(), g.TempRegister(),
2763                                 g.TempRegister()};
2764   InstructionCode code = opcode | AddressingModeField::encode(addressing_mode) |
2765                          AtomicWidthField::encode(width);
2766   selector->Emit(code, arraysize(outputs), outputs, arraysize(inputs), inputs,
2767                  arraysize(temps), temps);
2768 }
2769 
2770 }  // namespace
2771 
VisitWordCompareZero(Node * user,Node * value,FlagsContinuation * cont)2772 void InstructionSelector::VisitWordCompareZero(Node* user, Node* value,
2773                                                FlagsContinuation* cont) {
2774   Arm64OperandGenerator g(this);
2775   // Try to combine with comparisons against 0 by simply inverting the branch.
2776   while (value->opcode() == IrOpcode::kWord32Equal && CanCover(user, value)) {
2777     Int32BinopMatcher m(value);
2778     if (!m.right().Is(0)) break;
2779 
2780     user = value;
2781     value = m.left().node();
2782     cont->Negate();
2783   }
2784 
2785   // Try to match bit checks to create TBZ/TBNZ instructions.
2786   // Unlike the switch below, CanCover check is not needed here.
2787   // If there are several uses of the given operation, we will generate a TBZ
2788   // instruction for each. This is useful even if there are other uses of the
2789   // arithmetic result, because it moves dependencies further back.
2790   switch (value->opcode()) {
2791     case IrOpcode::kWord64Equal: {
2792       Int64BinopMatcher m(value);
2793       if (m.right().Is(0)) {
2794         Node* const left = m.left().node();
2795         if (left->opcode() == IrOpcode::kWord64And) {
2796           // Attempt to merge the Word64Equal(Word64And(x, y), 0) comparison
2797           // into a tbz/tbnz instruction.
2798           TestAndBranchMatcher<Uint64BinopMatcher> tbm(left, cont);
2799           if (tbm.Matches()) {
2800             Arm64OperandGenerator gen(this);
2801             cont->OverwriteAndNegateIfEqual(kEqual);
2802             this->EmitWithContinuation(kArm64TestAndBranch,
2803                                        gen.UseRegister(tbm.input()),
2804                                        gen.TempImmediate(tbm.bit()), cont);
2805             return;
2806           }
2807         }
2808       }
2809       break;
2810     }
2811     case IrOpcode::kWord32And: {
2812       TestAndBranchMatcher<Uint32BinopMatcher> tbm(value, cont);
2813       if (tbm.Matches()) {
2814         Arm64OperandGenerator gen(this);
2815         this->EmitWithContinuation(kArm64TestAndBranch32,
2816                                    gen.UseRegister(tbm.input()),
2817                                    gen.TempImmediate(tbm.bit()), cont);
2818         return;
2819       }
2820       break;
2821     }
2822     case IrOpcode::kWord64And: {
2823       TestAndBranchMatcher<Uint64BinopMatcher> tbm(value, cont);
2824       if (tbm.Matches()) {
2825         Arm64OperandGenerator gen(this);
2826         this->EmitWithContinuation(kArm64TestAndBranch,
2827                                    gen.UseRegister(tbm.input()),
2828                                    gen.TempImmediate(tbm.bit()), cont);
2829         return;
2830       }
2831       break;
2832     }
2833     default:
2834       break;
2835   }
2836 
2837   if (CanCover(user, value)) {
2838     switch (value->opcode()) {
2839       case IrOpcode::kWord32Equal:
2840         cont->OverwriteAndNegateIfEqual(kEqual);
2841         return VisitWord32Compare(this, value, cont);
2842       case IrOpcode::kInt32LessThan:
2843         cont->OverwriteAndNegateIfEqual(kSignedLessThan);
2844         return VisitWord32Compare(this, value, cont);
2845       case IrOpcode::kInt32LessThanOrEqual:
2846         cont->OverwriteAndNegateIfEqual(kSignedLessThanOrEqual);
2847         return VisitWord32Compare(this, value, cont);
2848       case IrOpcode::kUint32LessThan:
2849         cont->OverwriteAndNegateIfEqual(kUnsignedLessThan);
2850         return VisitWord32Compare(this, value, cont);
2851       case IrOpcode::kUint32LessThanOrEqual:
2852         cont->OverwriteAndNegateIfEqual(kUnsignedLessThanOrEqual);
2853         return VisitWord32Compare(this, value, cont);
2854       case IrOpcode::kWord64Equal: {
2855         cont->OverwriteAndNegateIfEqual(kEqual);
2856         Int64BinopMatcher m(value);
2857         if (m.right().Is(0)) {
2858           Node* const left = m.left().node();
2859           if (CanCover(value, left) && left->opcode() == IrOpcode::kWord64And) {
2860             return VisitWordCompare(this, left, kArm64Tst, cont, kLogical64Imm);
2861           }
2862         }
2863         return VisitWordCompare(this, value, kArm64Cmp, cont, kArithmeticImm);
2864       }
2865       case IrOpcode::kInt64LessThan:
2866         cont->OverwriteAndNegateIfEqual(kSignedLessThan);
2867         return VisitWordCompare(this, value, kArm64Cmp, cont, kArithmeticImm);
2868       case IrOpcode::kInt64LessThanOrEqual:
2869         cont->OverwriteAndNegateIfEqual(kSignedLessThanOrEqual);
2870         return VisitWordCompare(this, value, kArm64Cmp, cont, kArithmeticImm);
2871       case IrOpcode::kUint64LessThan:
2872         cont->OverwriteAndNegateIfEqual(kUnsignedLessThan);
2873         return VisitWordCompare(this, value, kArm64Cmp, cont, kArithmeticImm);
2874       case IrOpcode::kUint64LessThanOrEqual:
2875         cont->OverwriteAndNegateIfEqual(kUnsignedLessThanOrEqual);
2876         return VisitWordCompare(this, value, kArm64Cmp, cont, kArithmeticImm);
2877       case IrOpcode::kFloat32Equal:
2878         cont->OverwriteAndNegateIfEqual(kEqual);
2879         return VisitFloat32Compare(this, value, cont);
2880       case IrOpcode::kFloat32LessThan:
2881         cont->OverwriteAndNegateIfEqual(kFloatLessThan);
2882         return VisitFloat32Compare(this, value, cont);
2883       case IrOpcode::kFloat32LessThanOrEqual:
2884         cont->OverwriteAndNegateIfEqual(kFloatLessThanOrEqual);
2885         return VisitFloat32Compare(this, value, cont);
2886       case IrOpcode::kFloat64Equal:
2887         cont->OverwriteAndNegateIfEqual(kEqual);
2888         return VisitFloat64Compare(this, value, cont);
2889       case IrOpcode::kFloat64LessThan:
2890         cont->OverwriteAndNegateIfEqual(kFloatLessThan);
2891         return VisitFloat64Compare(this, value, cont);
2892       case IrOpcode::kFloat64LessThanOrEqual:
2893         cont->OverwriteAndNegateIfEqual(kFloatLessThanOrEqual);
2894         return VisitFloat64Compare(this, value, cont);
2895       case IrOpcode::kProjection:
2896         // Check if this is the overflow output projection of an
2897         // <Operation>WithOverflow node.
2898         if (ProjectionIndexOf(value->op()) == 1u) {
2899           // We cannot combine the <Operation>WithOverflow with this branch
2900           // unless the 0th projection (the use of the actual value of the
2901           // <Operation> is either nullptr, which means there's no use of the
2902           // actual value, or was already defined, which means it is scheduled
2903           // *AFTER* this branch).
2904           Node* const node = value->InputAt(0);
2905           Node* const result = NodeProperties::FindProjection(node, 0);
2906           if (result == nullptr || IsDefined(result)) {
2907             switch (node->opcode()) {
2908               case IrOpcode::kInt32AddWithOverflow:
2909                 cont->OverwriteAndNegateIfEqual(kOverflow);
2910                 return VisitBinop<Int32BinopMatcher>(this, node, kArm64Add32,
2911                                                      kArithmeticImm, cont);
2912               case IrOpcode::kInt32SubWithOverflow:
2913                 cont->OverwriteAndNegateIfEqual(kOverflow);
2914                 return VisitBinop<Int32BinopMatcher>(this, node, kArm64Sub32,
2915                                                      kArithmeticImm, cont);
2916               case IrOpcode::kInt32MulWithOverflow:
2917                 // ARM64 doesn't set the overflow flag for multiplication, so we
2918                 // need to test on kNotEqual. Here is the code sequence used:
2919                 //   smull result, left, right
2920                 //   cmp result.X(), Operand(result, SXTW)
2921                 cont->OverwriteAndNegateIfEqual(kNotEqual);
2922                 return EmitInt32MulWithOverflow(this, node, cont);
2923               case IrOpcode::kInt64AddWithOverflow:
2924                 cont->OverwriteAndNegateIfEqual(kOverflow);
2925                 return VisitBinop<Int64BinopMatcher>(this, node, kArm64Add,
2926                                                      kArithmeticImm, cont);
2927               case IrOpcode::kInt64SubWithOverflow:
2928                 cont->OverwriteAndNegateIfEqual(kOverflow);
2929                 return VisitBinop<Int64BinopMatcher>(this, node, kArm64Sub,
2930                                                      kArithmeticImm, cont);
2931               default:
2932                 break;
2933             }
2934           }
2935         }
2936         break;
2937       case IrOpcode::kInt32Add:
2938         return VisitWordCompare(this, value, kArm64Cmn32, cont, kArithmeticImm);
2939       case IrOpcode::kInt32Sub:
2940         return VisitWord32Compare(this, value, cont);
2941       case IrOpcode::kWord32And:
2942         return VisitWordCompare(this, value, kArm64Tst32, cont, kLogical32Imm);
2943       case IrOpcode::kWord64And:
2944         return VisitWordCompare(this, value, kArm64Tst, cont, kLogical64Imm);
2945       case IrOpcode::kStackPointerGreaterThan:
2946         cont->OverwriteAndNegateIfEqual(kStackPointerGreaterThanCondition);
2947         return VisitStackPointerGreaterThan(value, cont);
2948       default:
2949         break;
2950     }
2951   }
2952 
2953   // Branch could not be combined with a compare, compare against 0 and branch.
2954   if (cont->IsBranch()) {
2955     Emit(cont->Encode(kArm64CompareAndBranch32), g.NoOutput(),
2956          g.UseRegister(value), g.Label(cont->true_block()),
2957          g.Label(cont->false_block()));
2958   } else {
2959     VisitCompare(this, cont->Encode(kArm64Tst32), g.UseRegister(value),
2960                  g.UseRegister(value), cont);
2961   }
2962 }
2963 
VisitSwitch(Node * node,const SwitchInfo & sw)2964 void InstructionSelector::VisitSwitch(Node* node, const SwitchInfo& sw) {
2965   Arm64OperandGenerator g(this);
2966   InstructionOperand value_operand = g.UseRegister(node->InputAt(0));
2967 
2968   // Emit either ArchTableSwitch or ArchBinarySearchSwitch.
2969   if (enable_switch_jump_table_ == kEnableSwitchJumpTable) {
2970     static const size_t kMaxTableSwitchValueRange = 2 << 16;
2971     size_t table_space_cost = 4 + sw.value_range();
2972     size_t table_time_cost = 3;
2973     size_t lookup_space_cost = 3 + 2 * sw.case_count();
2974     size_t lookup_time_cost = sw.case_count();
2975     if (sw.case_count() > 4 &&
2976         table_space_cost + 3 * table_time_cost <=
2977             lookup_space_cost + 3 * lookup_time_cost &&
2978         sw.min_value() > std::numeric_limits<int32_t>::min() &&
2979         sw.value_range() <= kMaxTableSwitchValueRange) {
2980       InstructionOperand index_operand = value_operand;
2981       if (sw.min_value()) {
2982         index_operand = g.TempRegister();
2983         Emit(kArm64Sub32, index_operand, value_operand,
2984              g.TempImmediate(sw.min_value()));
2985       }
2986       // Generate a table lookup.
2987       return EmitTableSwitch(sw, index_operand);
2988     }
2989   }
2990 
2991   // Generate a tree of conditional jumps.
2992   return EmitBinarySearchSwitch(sw, value_operand);
2993 }
2994 
VisitWord32Equal(Node * const node)2995 void InstructionSelector::VisitWord32Equal(Node* const node) {
2996   Node* const user = node;
2997   FlagsContinuation cont = FlagsContinuation::ForSet(kEqual, node);
2998   Int32BinopMatcher m(user);
2999   if (m.right().Is(0)) {
3000     Node* const value = m.left().node();
3001     if (CanCover(user, value)) {
3002       switch (value->opcode()) {
3003         case IrOpcode::kInt32Add:
3004         case IrOpcode::kWord32And:
3005           return VisitWord32Compare(this, node, &cont);
3006         case IrOpcode::kInt32Sub:
3007           return VisitWordCompare(this, value, kArm64Cmp32, &cont,
3008                                   kArithmeticImm);
3009         case IrOpcode::kWord32Equal: {
3010           // Word32Equal(Word32Equal(x, y), 0) => Word32Compare(x, y, ne).
3011           Int32BinopMatcher mequal(value);
3012           node->ReplaceInput(0, mequal.left().node());
3013           node->ReplaceInput(1, mequal.right().node());
3014           cont.Negate();
3015           // {node} still does not cover its new operands, because {mequal} is
3016           // still using them.
3017           // Since we won't generate any more code for {mequal}, set its
3018           // operands to zero to make sure {node} can cover them.
3019           // This improves pattern matching in VisitWord32Compare.
3020           mequal.node()->ReplaceInput(0, m.right().node());
3021           mequal.node()->ReplaceInput(1, m.right().node());
3022           return VisitWord32Compare(this, node, &cont);
3023         }
3024         default:
3025           break;
3026       }
3027       return VisitWord32Test(this, value, &cont);
3028     }
3029   }
3030   VisitWord32Compare(this, node, &cont);
3031 }
3032 
VisitInt32LessThan(Node * node)3033 void InstructionSelector::VisitInt32LessThan(Node* node) {
3034   FlagsContinuation cont = FlagsContinuation::ForSet(kSignedLessThan, node);
3035   VisitWord32Compare(this, node, &cont);
3036 }
3037 
VisitInt32LessThanOrEqual(Node * node)3038 void InstructionSelector::VisitInt32LessThanOrEqual(Node* node) {
3039   FlagsContinuation cont =
3040       FlagsContinuation::ForSet(kSignedLessThanOrEqual, node);
3041   VisitWord32Compare(this, node, &cont);
3042 }
3043 
VisitUint32LessThan(Node * node)3044 void InstructionSelector::VisitUint32LessThan(Node* node) {
3045   FlagsContinuation cont = FlagsContinuation::ForSet(kUnsignedLessThan, node);
3046   VisitWord32Compare(this, node, &cont);
3047 }
3048 
VisitUint32LessThanOrEqual(Node * node)3049 void InstructionSelector::VisitUint32LessThanOrEqual(Node* node) {
3050   FlagsContinuation cont =
3051       FlagsContinuation::ForSet(kUnsignedLessThanOrEqual, node);
3052   VisitWord32Compare(this, node, &cont);
3053 }
3054 
VisitWord64Equal(Node * const node)3055 void InstructionSelector::VisitWord64Equal(Node* const node) {
3056   Node* const user = node;
3057   FlagsContinuation cont = FlagsContinuation::ForSet(kEqual, node);
3058   Int64BinopMatcher m(user);
3059   if (m.right().Is(0)) {
3060     Node* const value = m.left().node();
3061     if (CanCover(user, value)) {
3062       switch (value->opcode()) {
3063         case IrOpcode::kWord64And:
3064           return VisitWordCompare(this, value, kArm64Tst, &cont, kLogical64Imm);
3065         default:
3066           break;
3067       }
3068       return VisitWord64Test(this, value, &cont);
3069     }
3070   }
3071   VisitWordCompare(this, node, kArm64Cmp, &cont, kArithmeticImm);
3072 }
3073 
VisitInt32AddWithOverflow(Node * node)3074 void InstructionSelector::VisitInt32AddWithOverflow(Node* node) {
3075   if (Node* ovf = NodeProperties::FindProjection(node, 1)) {
3076     FlagsContinuation cont = FlagsContinuation::ForSet(kOverflow, ovf);
3077     return VisitBinop<Int32BinopMatcher>(this, node, kArm64Add32,
3078                                          kArithmeticImm, &cont);
3079   }
3080   FlagsContinuation cont;
3081   VisitBinop<Int32BinopMatcher>(this, node, kArm64Add32, kArithmeticImm, &cont);
3082 }
3083 
VisitInt32SubWithOverflow(Node * node)3084 void InstructionSelector::VisitInt32SubWithOverflow(Node* node) {
3085   if (Node* ovf = NodeProperties::FindProjection(node, 1)) {
3086     FlagsContinuation cont = FlagsContinuation::ForSet(kOverflow, ovf);
3087     return VisitBinop<Int32BinopMatcher>(this, node, kArm64Sub32,
3088                                          kArithmeticImm, &cont);
3089   }
3090   FlagsContinuation cont;
3091   VisitBinop<Int32BinopMatcher>(this, node, kArm64Sub32, kArithmeticImm, &cont);
3092 }
3093 
VisitInt32MulWithOverflow(Node * node)3094 void InstructionSelector::VisitInt32MulWithOverflow(Node* node) {
3095   if (Node* ovf = NodeProperties::FindProjection(node, 1)) {
3096     // ARM64 doesn't set the overflow flag for multiplication, so we need to
3097     // test on kNotEqual. Here is the code sequence used:
3098     //   smull result, left, right
3099     //   cmp result.X(), Operand(result, SXTW)
3100     FlagsContinuation cont = FlagsContinuation::ForSet(kNotEqual, ovf);
3101     return EmitInt32MulWithOverflow(this, node, &cont);
3102   }
3103   FlagsContinuation cont;
3104   EmitInt32MulWithOverflow(this, node, &cont);
3105 }
3106 
VisitInt64AddWithOverflow(Node * node)3107 void InstructionSelector::VisitInt64AddWithOverflow(Node* node) {
3108   if (Node* ovf = NodeProperties::FindProjection(node, 1)) {
3109     FlagsContinuation cont = FlagsContinuation::ForSet(kOverflow, ovf);
3110     return VisitBinop<Int64BinopMatcher>(this, node, kArm64Add, kArithmeticImm,
3111                                          &cont);
3112   }
3113   FlagsContinuation cont;
3114   VisitBinop<Int64BinopMatcher>(this, node, kArm64Add, kArithmeticImm, &cont);
3115 }
3116 
VisitInt64SubWithOverflow(Node * node)3117 void InstructionSelector::VisitInt64SubWithOverflow(Node* node) {
3118   if (Node* ovf = NodeProperties::FindProjection(node, 1)) {
3119     FlagsContinuation cont = FlagsContinuation::ForSet(kOverflow, ovf);
3120     return VisitBinop<Int64BinopMatcher>(this, node, kArm64Sub, kArithmeticImm,
3121                                          &cont);
3122   }
3123   FlagsContinuation cont;
3124   VisitBinop<Int64BinopMatcher>(this, node, kArm64Sub, kArithmeticImm, &cont);
3125 }
3126 
VisitInt64LessThan(Node * node)3127 void InstructionSelector::VisitInt64LessThan(Node* node) {
3128   FlagsContinuation cont = FlagsContinuation::ForSet(kSignedLessThan, node);
3129   VisitWordCompare(this, node, kArm64Cmp, &cont, kArithmeticImm);
3130 }
3131 
VisitInt64LessThanOrEqual(Node * node)3132 void InstructionSelector::VisitInt64LessThanOrEqual(Node* node) {
3133   FlagsContinuation cont =
3134       FlagsContinuation::ForSet(kSignedLessThanOrEqual, node);
3135   VisitWordCompare(this, node, kArm64Cmp, &cont, kArithmeticImm);
3136 }
3137 
VisitUint64LessThan(Node * node)3138 void InstructionSelector::VisitUint64LessThan(Node* node) {
3139   FlagsContinuation cont = FlagsContinuation::ForSet(kUnsignedLessThan, node);
3140   VisitWordCompare(this, node, kArm64Cmp, &cont, kArithmeticImm);
3141 }
3142 
VisitUint64LessThanOrEqual(Node * node)3143 void InstructionSelector::VisitUint64LessThanOrEqual(Node* node) {
3144   FlagsContinuation cont =
3145       FlagsContinuation::ForSet(kUnsignedLessThanOrEqual, node);
3146   VisitWordCompare(this, node, kArm64Cmp, &cont, kArithmeticImm);
3147 }
3148 
VisitFloat32Neg(Node * node)3149 void InstructionSelector::VisitFloat32Neg(Node* node) {
3150   Arm64OperandGenerator g(this);
3151   Node* in = node->InputAt(0);
3152   if (in->opcode() == IrOpcode::kFloat32Mul && CanCover(node, in)) {
3153     Float32BinopMatcher m(in);
3154     Emit(kArm64Float32Fnmul, g.DefineAsRegister(node),
3155          g.UseRegister(m.left().node()), g.UseRegister(m.right().node()));
3156     return;
3157   }
3158   VisitRR(this, kArm64Float32Neg, node);
3159 }
3160 
VisitFloat32Mul(Node * node)3161 void InstructionSelector::VisitFloat32Mul(Node* node) {
3162   Arm64OperandGenerator g(this);
3163   Float32BinopMatcher m(node);
3164 
3165   if (m.left().IsFloat32Neg() && CanCover(node, m.left().node())) {
3166     Emit(kArm64Float32Fnmul, g.DefineAsRegister(node),
3167          g.UseRegister(m.left().node()->InputAt(0)),
3168          g.UseRegister(m.right().node()));
3169     return;
3170   }
3171 
3172   if (m.right().IsFloat32Neg() && CanCover(node, m.right().node())) {
3173     Emit(kArm64Float32Fnmul, g.DefineAsRegister(node),
3174          g.UseRegister(m.right().node()->InputAt(0)),
3175          g.UseRegister(m.left().node()));
3176     return;
3177   }
3178   return VisitRRR(this, kArm64Float32Mul, node);
3179 }
3180 
VisitFloat32Abs(Node * node)3181 void InstructionSelector::VisitFloat32Abs(Node* node) {
3182   Arm64OperandGenerator g(this);
3183   Node* in = node->InputAt(0);
3184   if (in->opcode() == IrOpcode::kFloat32Sub && CanCover(node, in)) {
3185     Emit(kArm64Float32Abd, g.DefineAsRegister(node),
3186          g.UseRegister(in->InputAt(0)), g.UseRegister(in->InputAt(1)));
3187     return;
3188   }
3189 
3190   return VisitRR(this, kArm64Float32Abs, node);
3191 }
3192 
VisitFloat64Abs(Node * node)3193 void InstructionSelector::VisitFloat64Abs(Node* node) {
3194   Arm64OperandGenerator g(this);
3195   Node* in = node->InputAt(0);
3196   if (in->opcode() == IrOpcode::kFloat64Sub && CanCover(node, in)) {
3197     Emit(kArm64Float64Abd, g.DefineAsRegister(node),
3198          g.UseRegister(in->InputAt(0)), g.UseRegister(in->InputAt(1)));
3199     return;
3200   }
3201 
3202   return VisitRR(this, kArm64Float64Abs, node);
3203 }
3204 
VisitFloat32Equal(Node * node)3205 void InstructionSelector::VisitFloat32Equal(Node* node) {
3206   FlagsContinuation cont = FlagsContinuation::ForSet(kEqual, node);
3207   VisitFloat32Compare(this, node, &cont);
3208 }
3209 
VisitFloat32LessThan(Node * node)3210 void InstructionSelector::VisitFloat32LessThan(Node* node) {
3211   FlagsContinuation cont = FlagsContinuation::ForSet(kFloatLessThan, node);
3212   VisitFloat32Compare(this, node, &cont);
3213 }
3214 
VisitFloat32LessThanOrEqual(Node * node)3215 void InstructionSelector::VisitFloat32LessThanOrEqual(Node* node) {
3216   FlagsContinuation cont =
3217       FlagsContinuation::ForSet(kFloatLessThanOrEqual, node);
3218   VisitFloat32Compare(this, node, &cont);
3219 }
3220 
VisitFloat64Equal(Node * node)3221 void InstructionSelector::VisitFloat64Equal(Node* node) {
3222   FlagsContinuation cont = FlagsContinuation::ForSet(kEqual, node);
3223   VisitFloat64Compare(this, node, &cont);
3224 }
3225 
VisitFloat64LessThan(Node * node)3226 void InstructionSelector::VisitFloat64LessThan(Node* node) {
3227   FlagsContinuation cont = FlagsContinuation::ForSet(kFloatLessThan, node);
3228   VisitFloat64Compare(this, node, &cont);
3229 }
3230 
VisitFloat64LessThanOrEqual(Node * node)3231 void InstructionSelector::VisitFloat64LessThanOrEqual(Node* node) {
3232   FlagsContinuation cont =
3233       FlagsContinuation::ForSet(kFloatLessThanOrEqual, node);
3234   VisitFloat64Compare(this, node, &cont);
3235 }
3236 
VisitFloat64InsertLowWord32(Node * node)3237 void InstructionSelector::VisitFloat64InsertLowWord32(Node* node) {
3238   Arm64OperandGenerator g(this);
3239   Node* left = node->InputAt(0);
3240   Node* right = node->InputAt(1);
3241   if (left->opcode() == IrOpcode::kFloat64InsertHighWord32 &&
3242       CanCover(node, left)) {
3243     Node* right_of_left = left->InputAt(1);
3244     Emit(kArm64Bfi, g.DefineSameAsFirst(right), g.UseRegister(right),
3245          g.UseRegister(right_of_left), g.TempImmediate(32),
3246          g.TempImmediate(32));
3247     Emit(kArm64Float64MoveU64, g.DefineAsRegister(node), g.UseRegister(right));
3248     return;
3249   }
3250   Emit(kArm64Float64InsertLowWord32, g.DefineSameAsFirst(node),
3251        g.UseRegister(left), g.UseRegister(right));
3252 }
3253 
VisitFloat64InsertHighWord32(Node * node)3254 void InstructionSelector::VisitFloat64InsertHighWord32(Node* node) {
3255   Arm64OperandGenerator g(this);
3256   Node* left = node->InputAt(0);
3257   Node* right = node->InputAt(1);
3258   if (left->opcode() == IrOpcode::kFloat64InsertLowWord32 &&
3259       CanCover(node, left)) {
3260     Node* right_of_left = left->InputAt(1);
3261     Emit(kArm64Bfi, g.DefineSameAsFirst(left), g.UseRegister(right_of_left),
3262          g.UseRegister(right), g.TempImmediate(32), g.TempImmediate(32));
3263     Emit(kArm64Float64MoveU64, g.DefineAsRegister(node), g.UseRegister(left));
3264     return;
3265   }
3266   Emit(kArm64Float64InsertHighWord32, g.DefineSameAsFirst(node),
3267        g.UseRegister(left), g.UseRegister(right));
3268 }
3269 
VisitFloat64Neg(Node * node)3270 void InstructionSelector::VisitFloat64Neg(Node* node) {
3271   Arm64OperandGenerator g(this);
3272   Node* in = node->InputAt(0);
3273   if (in->opcode() == IrOpcode::kFloat64Mul && CanCover(node, in)) {
3274     Float64BinopMatcher m(in);
3275     Emit(kArm64Float64Fnmul, g.DefineAsRegister(node),
3276          g.UseRegister(m.left().node()), g.UseRegister(m.right().node()));
3277     return;
3278   }
3279   VisitRR(this, kArm64Float64Neg, node);
3280 }
3281 
VisitFloat64Mul(Node * node)3282 void InstructionSelector::VisitFloat64Mul(Node* node) {
3283   Arm64OperandGenerator g(this);
3284   Float64BinopMatcher m(node);
3285 
3286   if (m.left().IsFloat64Neg() && CanCover(node, m.left().node())) {
3287     Emit(kArm64Float64Fnmul, g.DefineAsRegister(node),
3288          g.UseRegister(m.left().node()->InputAt(0)),
3289          g.UseRegister(m.right().node()));
3290     return;
3291   }
3292 
3293   if (m.right().IsFloat64Neg() && CanCover(node, m.right().node())) {
3294     Emit(kArm64Float64Fnmul, g.DefineAsRegister(node),
3295          g.UseRegister(m.right().node()->InputAt(0)),
3296          g.UseRegister(m.left().node()));
3297     return;
3298   }
3299   return VisitRRR(this, kArm64Float64Mul, node);
3300 }
3301 
VisitMemoryBarrier(Node * node)3302 void InstructionSelector::VisitMemoryBarrier(Node* node) {
3303   Arm64OperandGenerator g(this);
3304   Emit(kArm64DmbIsh, g.NoOutput());
3305 }
3306 
VisitWord32AtomicLoad(Node * node)3307 void InstructionSelector::VisitWord32AtomicLoad(Node* node) {
3308   VisitAtomicLoad(this, node, AtomicWidth::kWord32);
3309 }
3310 
VisitWord64AtomicLoad(Node * node)3311 void InstructionSelector::VisitWord64AtomicLoad(Node* node) {
3312   VisitAtomicLoad(this, node, AtomicWidth::kWord64);
3313 }
3314 
VisitWord32AtomicStore(Node * node)3315 void InstructionSelector::VisitWord32AtomicStore(Node* node) {
3316   VisitAtomicStore(this, node, AtomicWidth::kWord32);
3317 }
3318 
VisitWord64AtomicStore(Node * node)3319 void InstructionSelector::VisitWord64AtomicStore(Node* node) {
3320   VisitAtomicStore(this, node, AtomicWidth::kWord64);
3321 }
3322 
VisitWord32AtomicExchange(Node * node)3323 void InstructionSelector::VisitWord32AtomicExchange(Node* node) {
3324   ArchOpcode opcode;
3325   MachineType type = AtomicOpType(node->op());
3326   if (type == MachineType::Int8()) {
3327     opcode = kAtomicExchangeInt8;
3328   } else if (type == MachineType::Uint8()) {
3329     opcode = kAtomicExchangeUint8;
3330   } else if (type == MachineType::Int16()) {
3331     opcode = kAtomicExchangeInt16;
3332   } else if (type == MachineType::Uint16()) {
3333     opcode = kAtomicExchangeUint16;
3334   } else if (type == MachineType::Int32() || type == MachineType::Uint32()) {
3335     opcode = kAtomicExchangeWord32;
3336   } else {
3337     UNREACHABLE();
3338   }
3339   VisitAtomicExchange(this, node, opcode, AtomicWidth::kWord32);
3340 }
3341 
VisitWord64AtomicExchange(Node * node)3342 void InstructionSelector::VisitWord64AtomicExchange(Node* node) {
3343   ArchOpcode opcode;
3344   MachineType type = AtomicOpType(node->op());
3345   if (type == MachineType::Uint8()) {
3346     opcode = kAtomicExchangeUint8;
3347   } else if (type == MachineType::Uint16()) {
3348     opcode = kAtomicExchangeUint16;
3349   } else if (type == MachineType::Uint32()) {
3350     opcode = kAtomicExchangeWord32;
3351   } else if (type == MachineType::Uint64()) {
3352     opcode = kArm64Word64AtomicExchangeUint64;
3353   } else {
3354     UNREACHABLE();
3355   }
3356   VisitAtomicExchange(this, node, opcode, AtomicWidth::kWord64);
3357 }
3358 
VisitWord32AtomicCompareExchange(Node * node)3359 void InstructionSelector::VisitWord32AtomicCompareExchange(Node* node) {
3360   ArchOpcode opcode;
3361   MachineType type = AtomicOpType(node->op());
3362   if (type == MachineType::Int8()) {
3363     opcode = kAtomicCompareExchangeInt8;
3364   } else if (type == MachineType::Uint8()) {
3365     opcode = kAtomicCompareExchangeUint8;
3366   } else if (type == MachineType::Int16()) {
3367     opcode = kAtomicCompareExchangeInt16;
3368   } else if (type == MachineType::Uint16()) {
3369     opcode = kAtomicCompareExchangeUint16;
3370   } else if (type == MachineType::Int32() || type == MachineType::Uint32()) {
3371     opcode = kAtomicCompareExchangeWord32;
3372   } else {
3373     UNREACHABLE();
3374   }
3375   VisitAtomicCompareExchange(this, node, opcode, AtomicWidth::kWord32);
3376 }
3377 
VisitWord64AtomicCompareExchange(Node * node)3378 void InstructionSelector::VisitWord64AtomicCompareExchange(Node* node) {
3379   ArchOpcode opcode;
3380   MachineType type = AtomicOpType(node->op());
3381   if (type == MachineType::Uint8()) {
3382     opcode = kAtomicCompareExchangeUint8;
3383   } else if (type == MachineType::Uint16()) {
3384     opcode = kAtomicCompareExchangeUint16;
3385   } else if (type == MachineType::Uint32()) {
3386     opcode = kAtomicCompareExchangeWord32;
3387   } else if (type == MachineType::Uint64()) {
3388     opcode = kArm64Word64AtomicCompareExchangeUint64;
3389   } else {
3390     UNREACHABLE();
3391   }
3392   VisitAtomicCompareExchange(this, node, opcode, AtomicWidth::kWord64);
3393 }
3394 
VisitWord32AtomicBinaryOperation(Node * node,ArchOpcode int8_op,ArchOpcode uint8_op,ArchOpcode int16_op,ArchOpcode uint16_op,ArchOpcode word32_op)3395 void InstructionSelector::VisitWord32AtomicBinaryOperation(
3396     Node* node, ArchOpcode int8_op, ArchOpcode uint8_op, ArchOpcode int16_op,
3397     ArchOpcode uint16_op, ArchOpcode word32_op) {
3398   ArchOpcode opcode;
3399   MachineType type = AtomicOpType(node->op());
3400   if (type == MachineType::Int8()) {
3401     opcode = int8_op;
3402   } else if (type == MachineType::Uint8()) {
3403     opcode = uint8_op;
3404   } else if (type == MachineType::Int16()) {
3405     opcode = int16_op;
3406   } else if (type == MachineType::Uint16()) {
3407     opcode = uint16_op;
3408   } else if (type == MachineType::Int32() || type == MachineType::Uint32()) {
3409     opcode = word32_op;
3410   } else {
3411     UNREACHABLE();
3412   }
3413   VisitAtomicBinop(this, node, opcode, AtomicWidth::kWord32);
3414 }
3415 
3416 #define VISIT_ATOMIC_BINOP(op)                                           \
3417   void InstructionSelector::VisitWord32Atomic##op(Node* node) {          \
3418     VisitWord32AtomicBinaryOperation(                                    \
3419         node, kAtomic##op##Int8, kAtomic##op##Uint8, kAtomic##op##Int16, \
3420         kAtomic##op##Uint16, kAtomic##op##Word32);                       \
3421   }
3422 VISIT_ATOMIC_BINOP(Add)
VISIT_ATOMIC_BINOP(Sub)3423 VISIT_ATOMIC_BINOP(Sub)
3424 VISIT_ATOMIC_BINOP(And)
3425 VISIT_ATOMIC_BINOP(Or)
3426 VISIT_ATOMIC_BINOP(Xor)
3427 #undef VISIT_ATOMIC_BINOP
3428 
3429 void InstructionSelector::VisitWord64AtomicBinaryOperation(
3430     Node* node, ArchOpcode uint8_op, ArchOpcode uint16_op, ArchOpcode uint32_op,
3431     ArchOpcode uint64_op) {
3432   ArchOpcode opcode;
3433   MachineType type = AtomicOpType(node->op());
3434   if (type == MachineType::Uint8()) {
3435     opcode = uint8_op;
3436   } else if (type == MachineType::Uint16()) {
3437     opcode = uint16_op;
3438   } else if (type == MachineType::Uint32()) {
3439     opcode = uint32_op;
3440   } else if (type == MachineType::Uint64()) {
3441     opcode = uint64_op;
3442   } else {
3443     UNREACHABLE();
3444   }
3445   VisitAtomicBinop(this, node, opcode, AtomicWidth::kWord64);
3446 }
3447 
3448 #define VISIT_ATOMIC_BINOP(op)                                                 \
3449   void InstructionSelector::VisitWord64Atomic##op(Node* node) {                \
3450     VisitWord64AtomicBinaryOperation(node, kAtomic##op##Uint8,                 \
3451                                      kAtomic##op##Uint16, kAtomic##op##Word32, \
3452                                      kArm64Word64Atomic##op##Uint64);          \
3453   }
3454 VISIT_ATOMIC_BINOP(Add)
VISIT_ATOMIC_BINOP(Sub)3455 VISIT_ATOMIC_BINOP(Sub)
3456 VISIT_ATOMIC_BINOP(And)
3457 VISIT_ATOMIC_BINOP(Or)
3458 VISIT_ATOMIC_BINOP(Xor)
3459 #undef VISIT_ATOMIC_BINOP
3460 
3461 void InstructionSelector::VisitInt32AbsWithOverflow(Node* node) {
3462   UNREACHABLE();
3463 }
3464 
VisitInt64AbsWithOverflow(Node * node)3465 void InstructionSelector::VisitInt64AbsWithOverflow(Node* node) {
3466   UNREACHABLE();
3467 }
3468 
3469 #define SIMD_UNOP_LIST(V)                                       \
3470   V(F64x2ConvertLowI32x4S, kArm64F64x2ConvertLowI32x4S)         \
3471   V(F64x2ConvertLowI32x4U, kArm64F64x2ConvertLowI32x4U)         \
3472   V(F64x2PromoteLowF32x4, kArm64F64x2PromoteLowF32x4)           \
3473   V(F32x4SConvertI32x4, kArm64F32x4SConvertI32x4)               \
3474   V(F32x4UConvertI32x4, kArm64F32x4UConvertI32x4)               \
3475   V(F32x4RecipApprox, kArm64F32x4RecipApprox)                   \
3476   V(F32x4RecipSqrtApprox, kArm64F32x4RecipSqrtApprox)           \
3477   V(F32x4DemoteF64x2Zero, kArm64F32x4DemoteF64x2Zero)           \
3478   V(I64x2BitMask, kArm64I64x2BitMask)                           \
3479   V(I32x4SConvertF32x4, kArm64I32x4SConvertF32x4)               \
3480   V(I32x4UConvertF32x4, kArm64I32x4UConvertF32x4)               \
3481   V(I32x4RelaxedTruncF32x4S, kArm64I32x4SConvertF32x4)          \
3482   V(I32x4RelaxedTruncF32x4U, kArm64I32x4UConvertF32x4)          \
3483   V(I32x4BitMask, kArm64I32x4BitMask)                           \
3484   V(I32x4TruncSatF64x2SZero, kArm64I32x4TruncSatF64x2SZero)     \
3485   V(I32x4TruncSatF64x2UZero, kArm64I32x4TruncSatF64x2UZero)     \
3486   V(I32x4RelaxedTruncF64x2SZero, kArm64I32x4TruncSatF64x2SZero) \
3487   V(I32x4RelaxedTruncF64x2UZero, kArm64I32x4TruncSatF64x2UZero) \
3488   V(I16x8BitMask, kArm64I16x8BitMask)                           \
3489   V(I8x16BitMask, kArm64I8x16BitMask)                           \
3490   V(S128Not, kArm64S128Not)                                     \
3491   V(V128AnyTrue, kArm64V128AnyTrue)                             \
3492   V(I64x2AllTrue, kArm64I64x2AllTrue)                           \
3493   V(I32x4AllTrue, kArm64I32x4AllTrue)                           \
3494   V(I16x8AllTrue, kArm64I16x8AllTrue)                           \
3495   V(I8x16AllTrue, kArm64I8x16AllTrue)
3496 
3497 #define SIMD_UNOP_LANE_SIZE_LIST(V) \
3498   V(F64x2Splat, kArm64FSplat, 64)   \
3499   V(F64x2Abs, kArm64FAbs, 64)       \
3500   V(F64x2Sqrt, kArm64FSqrt, 64)     \
3501   V(F64x2Neg, kArm64FNeg, 64)       \
3502   V(F32x4Splat, kArm64FSplat, 32)   \
3503   V(F32x4Abs, kArm64FAbs, 32)       \
3504   V(F32x4Sqrt, kArm64FSqrt, 32)     \
3505   V(F32x4Neg, kArm64FNeg, 32)       \
3506   V(I64x2Splat, kArm64ISplat, 64)   \
3507   V(I64x2Abs, kArm64IAbs, 64)       \
3508   V(I64x2Neg, kArm64INeg, 64)       \
3509   V(I32x4Splat, kArm64ISplat, 32)   \
3510   V(I32x4Abs, kArm64IAbs, 32)       \
3511   V(I32x4Neg, kArm64INeg, 32)       \
3512   V(I16x8Splat, kArm64ISplat, 16)   \
3513   V(I16x8Abs, kArm64IAbs, 16)       \
3514   V(I16x8Neg, kArm64INeg, 16)       \
3515   V(I8x16Splat, kArm64ISplat, 8)    \
3516   V(I8x16Abs, kArm64IAbs, 8)        \
3517   V(I8x16Neg, kArm64INeg, 8)
3518 
3519 #define SIMD_SHIFT_OP_LIST(V) \
3520   V(I64x2Shl, 64)             \
3521   V(I64x2ShrS, 64)            \
3522   V(I64x2ShrU, 64)            \
3523   V(I32x4Shl, 32)             \
3524   V(I32x4ShrS, 32)            \
3525   V(I32x4ShrU, 32)            \
3526   V(I16x8Shl, 16)             \
3527   V(I16x8ShrS, 16)            \
3528   V(I16x8ShrU, 16)            \
3529   V(I8x16Shl, 8)              \
3530   V(I8x16ShrS, 8)             \
3531   V(I8x16ShrU, 8)
3532 
3533 #define SIMD_BINOP_LIST(V)                              \
3534   V(I32x4Mul, kArm64I32x4Mul)                           \
3535   V(I32x4DotI16x8S, kArm64I32x4DotI16x8S)               \
3536   V(I16x8SConvertI32x4, kArm64I16x8SConvertI32x4)       \
3537   V(I16x8Mul, kArm64I16x8Mul)                           \
3538   V(I16x8UConvertI32x4, kArm64I16x8UConvertI32x4)       \
3539   V(I16x8Q15MulRSatS, kArm64I16x8Q15MulRSatS)           \
3540   V(I8x16SConvertI16x8, kArm64I8x16SConvertI16x8)       \
3541   V(I8x16UConvertI16x8, kArm64I8x16UConvertI16x8)       \
3542   V(S128And, kArm64S128And)                             \
3543   V(S128Or, kArm64S128Or)                               \
3544   V(S128Xor, kArm64S128Xor)                             \
3545   V(S128AndNot, kArm64S128AndNot)
3546 
3547 #define SIMD_BINOP_LANE_SIZE_LIST(V)                   \
3548   V(F64x2Min, kArm64FMin, 64)                          \
3549   V(F64x2Max, kArm64FMax, 64)                          \
3550   V(F64x2Add, kArm64FAdd, 64)                          \
3551   V(F64x2Sub, kArm64FSub, 64)                          \
3552   V(F64x2Div, kArm64FDiv, 64)                          \
3553   V(F64x2RelaxedMin, kArm64FMin, 64)                   \
3554   V(F64x2RelaxedMax, kArm64FMax, 64)                   \
3555   V(F32x4Min, kArm64FMin, 32)                          \
3556   V(F32x4Max, kArm64FMax, 32)                          \
3557   V(F32x4Add, kArm64FAdd, 32)                          \
3558   V(F32x4Sub, kArm64FSub, 32)                          \
3559   V(F32x4Div, kArm64FDiv, 32)                          \
3560   V(F32x4RelaxedMin, kArm64FMin, 32)                   \
3561   V(F32x4RelaxedMax, kArm64FMax, 32)                   \
3562   V(I64x2Sub, kArm64ISub, 64)                          \
3563   V(I32x4GtU, kArm64IGtU, 32)                          \
3564   V(I32x4GeU, kArm64IGeU, 32)                          \
3565   V(I32x4MinS, kArm64IMinS, 32)                        \
3566   V(I32x4MaxS, kArm64IMaxS, 32)                        \
3567   V(I32x4MinU, kArm64IMinU, 32)                        \
3568   V(I32x4MaxU, kArm64IMaxU, 32)                        \
3569   V(I16x8AddSatS, kArm64IAddSatS, 16)                  \
3570   V(I16x8SubSatS, kArm64ISubSatS, 16)                  \
3571   V(I16x8AddSatU, kArm64IAddSatU, 16)                  \
3572   V(I16x8SubSatU, kArm64ISubSatU, 16)                  \
3573   V(I16x8GtU, kArm64IGtU, 16)                          \
3574   V(I16x8GeU, kArm64IGeU, 16)                          \
3575   V(I16x8RoundingAverageU, kArm64RoundingAverageU, 16) \
3576   V(I8x16RoundingAverageU, kArm64RoundingAverageU, 8)  \
3577   V(I16x8MinS, kArm64IMinS, 16)                        \
3578   V(I16x8MaxS, kArm64IMaxS, 16)                        \
3579   V(I16x8MinU, kArm64IMinU, 16)                        \
3580   V(I16x8MaxU, kArm64IMaxU, 16)                        \
3581   V(I8x16Sub, kArm64ISub, 8)                           \
3582   V(I8x16AddSatS, kArm64IAddSatS, 8)                   \
3583   V(I8x16SubSatS, kArm64ISubSatS, 8)                   \
3584   V(I8x16AddSatU, kArm64IAddSatU, 8)                   \
3585   V(I8x16SubSatU, kArm64ISubSatU, 8)                   \
3586   V(I8x16GtU, kArm64IGtU, 8)                           \
3587   V(I8x16GeU, kArm64IGeU, 8)                           \
3588   V(I8x16MinS, kArm64IMinS, 8)                         \
3589   V(I8x16MaxS, kArm64IMaxS, 8)                         \
3590   V(I8x16MinU, kArm64IMinU, 8)                         \
3591   V(I8x16MaxU, kArm64IMaxU, 8)
3592 
VisitS128Const(Node * node)3593 void InstructionSelector::VisitS128Const(Node* node) {
3594   Arm64OperandGenerator g(this);
3595   static const int kUint32Immediates = 4;
3596   uint32_t val[kUint32Immediates];
3597   STATIC_ASSERT(sizeof(val) == kSimd128Size);
3598   memcpy(val, S128ImmediateParameterOf(node->op()).data(), kSimd128Size);
3599   // If all bytes are zeros, avoid emitting code for generic constants
3600   bool all_zeros = !(val[0] || val[1] || val[2] || val[3]);
3601   InstructionOperand dst = g.DefineAsRegister(node);
3602   if (all_zeros) {
3603     Emit(kArm64S128Zero, dst);
3604   } else {
3605     Emit(kArm64S128Const, g.DefineAsRegister(node), g.UseImmediate(val[0]),
3606          g.UseImmediate(val[1]), g.UseImmediate(val[2]),
3607          g.UseImmediate(val[3]));
3608   }
3609 }
3610 
VisitS128Zero(Node * node)3611 void InstructionSelector::VisitS128Zero(Node* node) {
3612   Arm64OperandGenerator g(this);
3613   Emit(kArm64S128Zero, g.DefineAsRegister(node));
3614 }
3615 
3616 #define SIMD_VISIT_EXTRACT_LANE(Type, T, Sign, LaneSize)                     \
3617   void InstructionSelector::Visit##Type##ExtractLane##Sign(Node* node) {     \
3618     VisitRRI(this,                                                           \
3619              kArm64##T##ExtractLane##Sign | LaneSizeField::encode(LaneSize), \
3620              node);                                                          \
3621   }
3622 SIMD_VISIT_EXTRACT_LANE(F64x2, F, , 64)
3623 SIMD_VISIT_EXTRACT_LANE(F32x4, F, , 32)
3624 SIMD_VISIT_EXTRACT_LANE(I64x2, I, , 64)
3625 SIMD_VISIT_EXTRACT_LANE(I32x4, I, , 32)
3626 SIMD_VISIT_EXTRACT_LANE(I16x8, I, U, 16)
3627 SIMD_VISIT_EXTRACT_LANE(I16x8, I, S, 16)
3628 SIMD_VISIT_EXTRACT_LANE(I8x16, I, U, 8)
3629 SIMD_VISIT_EXTRACT_LANE(I8x16, I, S, 8)
3630 #undef SIMD_VISIT_EXTRACT_LANE
3631 
3632 #define SIMD_VISIT_REPLACE_LANE(Type, T, LaneSize)                            \
3633   void InstructionSelector::Visit##Type##ReplaceLane(Node* node) {            \
3634     VisitRRIR(this, kArm64##T##ReplaceLane | LaneSizeField::encode(LaneSize), \
3635               node);                                                          \
3636   }
3637 SIMD_VISIT_REPLACE_LANE(F64x2, F, 64)
3638 SIMD_VISIT_REPLACE_LANE(F32x4, F, 32)
3639 SIMD_VISIT_REPLACE_LANE(I64x2, I, 64)
3640 SIMD_VISIT_REPLACE_LANE(I32x4, I, 32)
3641 SIMD_VISIT_REPLACE_LANE(I16x8, I, 16)
3642 SIMD_VISIT_REPLACE_LANE(I8x16, I, 8)
3643 #undef SIMD_VISIT_REPLACE_LANE
3644 
3645 #define SIMD_VISIT_UNOP(Name, instruction)            \
3646   void InstructionSelector::Visit##Name(Node* node) { \
3647     VisitRR(this, instruction, node);                 \
3648   }
3649 SIMD_UNOP_LIST(SIMD_VISIT_UNOP)
3650 #undef SIMD_VISIT_UNOP
3651 #undef SIMD_UNOP_LIST
3652 
3653 #define SIMD_VISIT_SHIFT_OP(Name, width)                \
3654   void InstructionSelector::Visit##Name(Node* node) {   \
3655     VisitSimdShiftRRR(this, kArm64##Name, node, width); \
3656   }
3657 SIMD_SHIFT_OP_LIST(SIMD_VISIT_SHIFT_OP)
3658 #undef SIMD_VISIT_SHIFT_OP
3659 #undef SIMD_SHIFT_OP_LIST
3660 
3661 #define SIMD_VISIT_BINOP(Name, instruction)           \
3662   void InstructionSelector::Visit##Name(Node* node) { \
3663     VisitRRR(this, instruction, node);                \
3664   }
3665 SIMD_BINOP_LIST(SIMD_VISIT_BINOP)
3666 #undef SIMD_VISIT_BINOP
3667 #undef SIMD_BINOP_LIST
3668 
3669 #define SIMD_VISIT_BINOP_LANE_SIZE(Name, instruction, LaneSize)          \
3670   void InstructionSelector::Visit##Name(Node* node) {                    \
3671     VisitRRR(this, instruction | LaneSizeField::encode(LaneSize), node); \
3672   }
3673 SIMD_BINOP_LANE_SIZE_LIST(SIMD_VISIT_BINOP_LANE_SIZE)
3674 #undef SIMD_VISIT_BINOP_LANE_SIZE
3675 #undef SIMD_BINOP_LANE_SIZE_LIST
3676 
3677 #define SIMD_VISIT_UNOP_LANE_SIZE(Name, instruction, LaneSize)          \
3678   void InstructionSelector::Visit##Name(Node* node) {                   \
3679     VisitRR(this, instruction | LaneSizeField::encode(LaneSize), node); \
3680   }
3681 SIMD_UNOP_LANE_SIZE_LIST(SIMD_VISIT_UNOP_LANE_SIZE)
3682 #undef SIMD_VISIT_UNOP_LANE_SIZE
3683 #undef SIMD_UNOP_LANE_SIZE_LIST
3684 
3685 using ShuffleMatcher =
3686     ValueMatcher<S128ImmediateParameter, IrOpcode::kI8x16Shuffle>;
3687 using BinopWithShuffleMatcher = BinopMatcher<ShuffleMatcher, ShuffleMatcher>;
3688 
3689 namespace {
3690 // Struct holding the result of pattern-matching a mul+dup.
3691 struct MulWithDupResult {
3692   Node* input;     // Node holding the vector elements.
3693   Node* dup_node;  // Node holding the lane to multiply.
3694   int index;
3695   // Pattern-match is successful if dup_node is set.
operator boolv8::internal::compiler::__anon1f5f66280811::MulWithDupResult3696   explicit operator bool() const { return dup_node != nullptr; }
3697 };
3698 
3699 template <int LANES>
TryMatchMulWithDup(Node * node)3700 MulWithDupResult TryMatchMulWithDup(Node* node) {
3701   // Pattern match:
3702   //   f32x4.mul(x, shuffle(x, y, indices)) => f32x4.mul(x, y, laneidx)
3703   //   f64x2.mul(x, shuffle(x, y, indices)) => f64x2.mul(x, y, laneidx)
3704   //   where shuffle(x, y, indices) = dup(x[laneidx]) or dup(y[laneidx])
3705   // f32x4.mul and f64x2.mul are commutative, so use BinopMatcher.
3706   Node* input = nullptr;
3707   Node* dup_node = nullptr;
3708 
3709   int index = 0;
3710 #if V8_ENABLE_WEBASSEMBLY
3711   BinopWithShuffleMatcher m = BinopWithShuffleMatcher(node);
3712   ShuffleMatcher left = m.left();
3713   ShuffleMatcher right = m.right();
3714 
3715   // TODO(zhin): We can canonicalize first to avoid checking index < LANES.
3716   // e.g. shuffle(x, y, [16, 17, 18, 19...]) => shuffle(y, y, [0, 1, 2,
3717   // 3]...). But doing so can mutate the inputs of the shuffle node without
3718   // updating the shuffle immediates themselves. Fix that before we
3719   // canonicalize here. We don't want CanCover here because in many use cases,
3720   // the shuffle is generated early in the function, but the f32x4.mul happens
3721   // in a loop, which won't cover the shuffle since they are different basic
3722   // blocks.
3723   if (left.HasResolvedValue() && wasm::SimdShuffle::TryMatchSplat<LANES>(
3724                                      left.ResolvedValue().data(), &index)) {
3725     dup_node = left.node()->InputAt(index < LANES ? 0 : 1);
3726     input = right.node();
3727   } else if (right.HasResolvedValue() &&
3728              wasm::SimdShuffle::TryMatchSplat<LANES>(
3729                  right.ResolvedValue().data(), &index)) {
3730     dup_node = right.node()->InputAt(index < LANES ? 0 : 1);
3731     input = left.node();
3732   }
3733 #endif  // V8_ENABLE_WEBASSEMBLY
3734 
3735   // Canonicalization would get rid of this too.
3736   index %= LANES;
3737 
3738   return {input, dup_node, index};
3739 }
3740 }  // namespace
3741 
VisitF32x4Mul(Node * node)3742 void InstructionSelector::VisitF32x4Mul(Node* node) {
3743   if (MulWithDupResult result = TryMatchMulWithDup<4>(node)) {
3744     Arm64OperandGenerator g(this);
3745     Emit(kArm64FMulElement | LaneSizeField::encode(32),
3746          g.DefineAsRegister(node), g.UseRegister(result.input),
3747          g.UseRegister(result.dup_node), g.UseImmediate(result.index));
3748   } else {
3749     return VisitRRR(this, kArm64FMul | LaneSizeField::encode(32), node);
3750   }
3751 }
3752 
VisitF64x2Mul(Node * node)3753 void InstructionSelector::VisitF64x2Mul(Node* node) {
3754   if (MulWithDupResult result = TryMatchMulWithDup<2>(node)) {
3755     Arm64OperandGenerator g(this);
3756     Emit(kArm64FMulElement | LaneSizeField::encode(64),
3757          g.DefineAsRegister(node), g.UseRegister(result.input),
3758          g.UseRegister(result.dup_node), g.UseImmediate(result.index));
3759   } else {
3760     return VisitRRR(this, kArm64FMul | LaneSizeField::encode(64), node);
3761   }
3762 }
3763 
VisitI64x2Mul(Node * node)3764 void InstructionSelector::VisitI64x2Mul(Node* node) {
3765   Arm64OperandGenerator g(this);
3766   InstructionOperand temps[] = {g.TempSimd128Register()};
3767   Emit(kArm64I64x2Mul, g.DefineAsRegister(node),
3768        g.UseRegister(node->InputAt(0)), g.UseRegister(node->InputAt(1)),
3769        arraysize(temps), temps);
3770 }
3771 
3772 namespace {
3773 
3774 // Used for pattern matching SIMD Add operations where one of the inputs matches
3775 // |opcode| and ensure that the matched input is on the LHS (input 0).
3776 struct SimdAddOpMatcher : public NodeMatcher {
SimdAddOpMatcherv8::internal::compiler::__anon1f5f66280911::SimdAddOpMatcher3777   explicit SimdAddOpMatcher(Node* node, IrOpcode::Value opcode)
3778       : NodeMatcher(node),
3779         opcode_(opcode),
3780         left_(InputAt(0)),
3781         right_(InputAt(1)) {
3782     DCHECK(HasProperty(Operator::kCommutative));
3783     PutOpOnLeft();
3784   }
3785 
Matchesv8::internal::compiler::__anon1f5f66280911::SimdAddOpMatcher3786   bool Matches() { return left_->opcode() == opcode_; }
leftv8::internal::compiler::__anon1f5f66280911::SimdAddOpMatcher3787   Node* left() const { return left_; }
rightv8::internal::compiler::__anon1f5f66280911::SimdAddOpMatcher3788   Node* right() const { return right_; }
3789 
3790  private:
PutOpOnLeftv8::internal::compiler::__anon1f5f66280911::SimdAddOpMatcher3791   void PutOpOnLeft() {
3792     if (right_->opcode() == opcode_) {
3793       std::swap(left_, right_);
3794       node()->ReplaceInput(0, left_);
3795       node()->ReplaceInput(1, right_);
3796     }
3797   }
3798   IrOpcode::Value opcode_;
3799   Node* left_;
3800   Node* right_;
3801 };
3802 
ShraHelper(InstructionSelector * selector,Node * node,int lane_size,InstructionCode shra_code,InstructionCode add_code,IrOpcode::Value shift_op)3803 bool ShraHelper(InstructionSelector* selector, Node* node, int lane_size,
3804                 InstructionCode shra_code, InstructionCode add_code,
3805                 IrOpcode::Value shift_op) {
3806   Arm64OperandGenerator g(selector);
3807   SimdAddOpMatcher m(node, shift_op);
3808   if (!m.Matches() || !selector->CanCover(node, m.left())) return false;
3809   if (!g.IsIntegerConstant(m.left()->InputAt(1))) return false;
3810 
3811   // If shifting by zero, just do the addition
3812   if (g.GetIntegerConstantValue(m.left()->InputAt(1)) % lane_size == 0) {
3813     selector->Emit(add_code, g.DefineAsRegister(node),
3814                    g.UseRegister(m.left()->InputAt(0)),
3815                    g.UseRegister(m.right()));
3816   } else {
3817     selector->Emit(shra_code | LaneSizeField::encode(lane_size),
3818                    g.DefineSameAsFirst(node), g.UseRegister(m.right()),
3819                    g.UseRegister(m.left()->InputAt(0)),
3820                    g.UseImmediate(m.left()->InputAt(1)));
3821   }
3822   return true;
3823 }
3824 
AdalpHelper(InstructionSelector * selector,Node * node,int lane_size,InstructionCode adalp_code,IrOpcode::Value ext_op)3825 bool AdalpHelper(InstructionSelector* selector, Node* node, int lane_size,
3826                  InstructionCode adalp_code, IrOpcode::Value ext_op) {
3827   Arm64OperandGenerator g(selector);
3828   SimdAddOpMatcher m(node, ext_op);
3829   if (!m.Matches() || !selector->CanCover(node, m.left())) return false;
3830   selector->Emit(adalp_code | LaneSizeField::encode(lane_size),
3831                  g.DefineSameAsFirst(node), g.UseRegister(m.right()),
3832                  g.UseRegister(m.left()->InputAt(0)));
3833   return true;
3834 }
3835 
MlaHelper(InstructionSelector * selector,Node * node,InstructionCode mla_code,IrOpcode::Value mul_op)3836 bool MlaHelper(InstructionSelector* selector, Node* node,
3837                InstructionCode mla_code, IrOpcode::Value mul_op) {
3838   Arm64OperandGenerator g(selector);
3839   SimdAddOpMatcher m(node, mul_op);
3840   if (!m.Matches() || !selector->CanCover(node, m.left())) return false;
3841   selector->Emit(mla_code, g.DefineSameAsFirst(node), g.UseRegister(m.right()),
3842                  g.UseRegister(m.left()->InputAt(0)),
3843                  g.UseRegister(m.left()->InputAt(1)));
3844   return true;
3845 }
3846 
SmlalHelper(InstructionSelector * selector,Node * node,int lane_size,InstructionCode smlal_code,IrOpcode::Value ext_mul_op)3847 bool SmlalHelper(InstructionSelector* selector, Node* node, int lane_size,
3848                  InstructionCode smlal_code, IrOpcode::Value ext_mul_op) {
3849   Arm64OperandGenerator g(selector);
3850   SimdAddOpMatcher m(node, ext_mul_op);
3851   if (!m.Matches() || !selector->CanCover(node, m.left())) return false;
3852 
3853   selector->Emit(smlal_code | LaneSizeField::encode(lane_size),
3854                  g.DefineSameAsFirst(node), g.UseRegister(m.right()),
3855                  g.UseRegister(m.left()->InputAt(0)),
3856                  g.UseRegister(m.left()->InputAt(1)));
3857   return true;
3858 }
3859 
3860 }  // namespace
3861 
VisitI64x2Add(Node * node)3862 void InstructionSelector::VisitI64x2Add(Node* node) {
3863   if (!ShraHelper(this, node, 64, kArm64Ssra,
3864                   kArm64IAdd | LaneSizeField::encode(64),
3865                   IrOpcode::kI64x2ShrS) &&
3866       !ShraHelper(this, node, 64, kArm64Usra,
3867                   kArm64IAdd | LaneSizeField::encode(64),
3868                   IrOpcode::kI64x2ShrU)) {
3869     VisitRRR(this, kArm64IAdd | LaneSizeField::encode(64), node);
3870   }
3871 }
3872 
VisitI8x16Add(Node * node)3873 void InstructionSelector::VisitI8x16Add(Node* node) {
3874   if (!ShraHelper(this, node, 8, kArm64Ssra,
3875                   kArm64IAdd | LaneSizeField::encode(8),
3876                   IrOpcode::kI8x16ShrS) &&
3877       !ShraHelper(this, node, 8, kArm64Usra,
3878                   kArm64IAdd | LaneSizeField::encode(8),
3879                   IrOpcode::kI8x16ShrU)) {
3880     VisitRRR(this, kArm64IAdd | LaneSizeField::encode(8), node);
3881   }
3882 }
3883 
3884 #define VISIT_SIMD_ADD(Type, PairwiseType, LaneSize)                       \
3885   void InstructionSelector::Visit##Type##Add(Node* node) {                 \
3886     /* Select Mla(z, x, y) for Add(x, Mul(y, z)). */                       \
3887     if (MlaHelper(this, node, kArm64Mla | LaneSizeField::encode(LaneSize), \
3888                   IrOpcode::k##Type##Mul)) {                               \
3889       return;                                                              \
3890     }                                                                      \
3891     /* Select S/Uadalp(x, y) for Add(x, ExtAddPairwise(y)). */             \
3892     if (AdalpHelper(this, node, LaneSize, kArm64Sadalp,                    \
3893                     IrOpcode::k##Type##ExtAddPairwise##PairwiseType##S) || \
3894         AdalpHelper(this, node, LaneSize, kArm64Uadalp,                    \
3895                     IrOpcode::k##Type##ExtAddPairwise##PairwiseType##U)) { \
3896       return;                                                              \
3897     }                                                                      \
3898     /* Select S/Usra(x, y) for Add(x, ShiftRight(y, imm)). */              \
3899     if (ShraHelper(this, node, LaneSize, kArm64Ssra,                       \
3900                    kArm64IAdd | LaneSizeField::encode(LaneSize),           \
3901                    IrOpcode::k##Type##ShrS) ||                             \
3902         ShraHelper(this, node, LaneSize, kArm64Usra,                       \
3903                    kArm64IAdd | LaneSizeField::encode(LaneSize),           \
3904                    IrOpcode::k##Type##ShrU)) {                             \
3905       return;                                                              \
3906     }                                                                      \
3907     /* Select Smlal/Umlal(x, y, z) for Add(x, ExtMulLow(y, z)) and         \
3908      * Smlal2/Umlal2(x, y, z) for Add(x, ExtMulHigh(y, z)). */             \
3909     if (SmlalHelper(this, node, LaneSize, kArm64Smlal,                     \
3910                     IrOpcode::k##Type##ExtMulLow##PairwiseType##S) ||      \
3911         SmlalHelper(this, node, LaneSize, kArm64Smlal2,                    \
3912                     IrOpcode::k##Type##ExtMulHigh##PairwiseType##S) ||     \
3913         SmlalHelper(this, node, LaneSize, kArm64Umlal,                     \
3914                     IrOpcode::k##Type##ExtMulLow##PairwiseType##U) ||      \
3915         SmlalHelper(this, node, LaneSize, kArm64Umlal2,                    \
3916                     IrOpcode::k##Type##ExtMulHigh##PairwiseType##U)) {     \
3917       return;                                                              \
3918     }                                                                      \
3919     VisitRRR(this, kArm64IAdd | LaneSizeField::encode(LaneSize), node);    \
3920   }
3921 
3922 VISIT_SIMD_ADD(I32x4, I16x8, 32)
3923 VISIT_SIMD_ADD(I16x8, I8x16, 16)
3924 #undef VISIT_SIMD_ADD
3925 
3926 #define VISIT_SIMD_SUB(Type, LaneSize)                                        \
3927   void InstructionSelector::Visit##Type##Sub(Node* node) {                    \
3928     Arm64OperandGenerator g(this);                                            \
3929     Node* left = node->InputAt(0);                                            \
3930     Node* right = node->InputAt(1);                                           \
3931     /* Select Mls(z, x, y) for Sub(z, Mul(x, y)). */                          \
3932     if (right->opcode() == IrOpcode::k##Type##Mul && CanCover(node, right)) { \
3933       Emit(kArm64Mls | LaneSizeField::encode(LaneSize),                       \
3934            g.DefineSameAsFirst(node), g.UseRegister(left),                    \
3935            g.UseRegister(right->InputAt(0)),                                  \
3936            g.UseRegister(right->InputAt(1)));                                 \
3937       return;                                                                 \
3938     }                                                                         \
3939     VisitRRR(this, kArm64ISub | LaneSizeField::encode(LaneSize), node);       \
3940   }
3941 
3942 VISIT_SIMD_SUB(I32x4, 32)
3943 VISIT_SIMD_SUB(I16x8, 16)
3944 #undef VISIT_SIMD_SUB
3945 
3946 namespace {
isSimdZero(Arm64OperandGenerator & g,Node * node)3947 bool isSimdZero(Arm64OperandGenerator& g, Node* node) {
3948   auto m = V128ConstMatcher(node);
3949   if (m.HasResolvedValue()) {
3950     auto imms = m.ResolvedValue().immediate();
3951     return (std::all_of(imms.begin(), imms.end(), std::logical_not<uint8_t>()));
3952   }
3953   return node->opcode() == IrOpcode::kS128Zero;
3954 }
3955 }  // namespace
3956 
3957 #define VISIT_SIMD_CM(Type, T, CmOp, CmOpposite, LaneSize)                   \
3958   void InstructionSelector::Visit##Type##CmOp(Node* node) {                  \
3959     Arm64OperandGenerator g(this);                                           \
3960     Node* left = node->InputAt(0);                                           \
3961     Node* right = node->InputAt(1);                                          \
3962     if (isSimdZero(g, left)) {                                               \
3963       Emit(kArm64##T##CmOpposite | LaneSizeField::encode(LaneSize),          \
3964            g.DefineAsRegister(node), g.UseRegister(right));                  \
3965       return;                                                                \
3966     } else if (isSimdZero(g, right)) {                                       \
3967       Emit(kArm64##T##CmOp | LaneSizeField::encode(LaneSize),                \
3968            g.DefineAsRegister(node), g.UseRegister(left));                   \
3969       return;                                                                \
3970     }                                                                        \
3971     VisitRRR(this, kArm64##T##CmOp | LaneSizeField::encode(LaneSize), node); \
3972   }
3973 
3974 VISIT_SIMD_CM(F64x2, F, Eq, Eq, 64)
3975 VISIT_SIMD_CM(F64x2, F, Ne, Ne, 64)
3976 VISIT_SIMD_CM(F64x2, F, Lt, Gt, 64)
3977 VISIT_SIMD_CM(F64x2, F, Le, Ge, 64)
3978 VISIT_SIMD_CM(F32x4, F, Eq, Eq, 32)
3979 VISIT_SIMD_CM(F32x4, F, Ne, Ne, 32)
3980 VISIT_SIMD_CM(F32x4, F, Lt, Gt, 32)
3981 VISIT_SIMD_CM(F32x4, F, Le, Ge, 32)
3982 
3983 VISIT_SIMD_CM(I64x2, I, Eq, Eq, 64)
3984 VISIT_SIMD_CM(I64x2, I, Ne, Ne, 64)
3985 VISIT_SIMD_CM(I64x2, I, GtS, LtS, 64)
3986 VISIT_SIMD_CM(I64x2, I, GeS, LeS, 64)
3987 VISIT_SIMD_CM(I32x4, I, Eq, Eq, 32)
3988 VISIT_SIMD_CM(I32x4, I, Ne, Ne, 32)
3989 VISIT_SIMD_CM(I32x4, I, GtS, LtS, 32)
3990 VISIT_SIMD_CM(I32x4, I, GeS, LeS, 32)
3991 VISIT_SIMD_CM(I16x8, I, Eq, Eq, 16)
3992 VISIT_SIMD_CM(I16x8, I, Ne, Ne, 16)
3993 VISIT_SIMD_CM(I16x8, I, GtS, LtS, 16)
3994 VISIT_SIMD_CM(I16x8, I, GeS, LeS, 16)
3995 VISIT_SIMD_CM(I8x16, I, Eq, Eq, 8)
3996 VISIT_SIMD_CM(I8x16, I, Ne, Ne, 8)
3997 VISIT_SIMD_CM(I8x16, I, GtS, LtS, 8)
3998 VISIT_SIMD_CM(I8x16, I, GeS, LeS, 8)
3999 #undef VISIT_SIMD_CM
4000 
VisitS128Select(Node * node)4001 void InstructionSelector::VisitS128Select(Node* node) {
4002   Arm64OperandGenerator g(this);
4003   Emit(kArm64S128Select, g.DefineSameAsFirst(node),
4004        g.UseRegister(node->InputAt(0)), g.UseRegister(node->InputAt(1)),
4005        g.UseRegister(node->InputAt(2)));
4006 }
4007 
VisitI8x16RelaxedLaneSelect(Node * node)4008 void InstructionSelector::VisitI8x16RelaxedLaneSelect(Node* node) {
4009   VisitS128Select(node);
4010 }
4011 
VisitI16x8RelaxedLaneSelect(Node * node)4012 void InstructionSelector::VisitI16x8RelaxedLaneSelect(Node* node) {
4013   VisitS128Select(node);
4014 }
4015 
VisitI32x4RelaxedLaneSelect(Node * node)4016 void InstructionSelector::VisitI32x4RelaxedLaneSelect(Node* node) {
4017   VisitS128Select(node);
4018 }
4019 
VisitI64x2RelaxedLaneSelect(Node * node)4020 void InstructionSelector::VisitI64x2RelaxedLaneSelect(Node* node) {
4021   VisitS128Select(node);
4022 }
4023 
4024 #define VISIT_SIMD_QFMOP(op)                                               \
4025   void InstructionSelector::Visit##op(Node* node) {                        \
4026     Arm64OperandGenerator g(this);                                         \
4027     Emit(kArm64##op, g.DefineSameAsFirst(node),                            \
4028          g.UseRegister(node->InputAt(0)), g.UseRegister(node->InputAt(1)), \
4029          g.UseRegister(node->InputAt(2)));                                 \
4030   }
4031 VISIT_SIMD_QFMOP(F64x2Qfma)
4032 VISIT_SIMD_QFMOP(F64x2Qfms)
4033 VISIT_SIMD_QFMOP(F32x4Qfma)
4034 VISIT_SIMD_QFMOP(F32x4Qfms)
4035 #undef VISIT_SIMD_QFMOP
4036 
4037 #if V8_ENABLE_WEBASSEMBLY
4038 namespace {
4039 
4040 struct ShuffleEntry {
4041   uint8_t shuffle[kSimd128Size];
4042   ArchOpcode opcode;
4043 };
4044 
4045 static const ShuffleEntry arch_shuffles[] = {
4046     {{0, 1, 2, 3, 16, 17, 18, 19, 4, 5, 6, 7, 20, 21, 22, 23},
4047      kArm64S32x4ZipLeft},
4048     {{8, 9, 10, 11, 24, 25, 26, 27, 12, 13, 14, 15, 28, 29, 30, 31},
4049      kArm64S32x4ZipRight},
4050     {{0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27},
4051      kArm64S32x4UnzipLeft},
4052     {{4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23, 28, 29, 30, 31},
4053      kArm64S32x4UnzipRight},
4054     {{0, 1, 2, 3, 16, 17, 18, 19, 8, 9, 10, 11, 24, 25, 26, 27},
4055      kArm64S32x4TransposeLeft},
4056     {{4, 5, 6, 7, 20, 21, 22, 23, 12, 13, 14, 15, 21, 22, 23, 24},
4057      kArm64S32x4TransposeRight},
4058     {{4, 5, 6, 7, 0, 1, 2, 3, 12, 13, 14, 15, 8, 9, 10, 11},
4059      kArm64S32x2Reverse},
4060 
4061     {{0, 1, 16, 17, 2, 3, 18, 19, 4, 5, 20, 21, 6, 7, 22, 23},
4062      kArm64S16x8ZipLeft},
4063     {{8, 9, 24, 25, 10, 11, 26, 27, 12, 13, 28, 29, 14, 15, 30, 31},
4064      kArm64S16x8ZipRight},
4065     {{0, 1, 4, 5, 8, 9, 12, 13, 16, 17, 20, 21, 24, 25, 28, 29},
4066      kArm64S16x8UnzipLeft},
4067     {{2, 3, 6, 7, 10, 11, 14, 15, 18, 19, 22, 23, 26, 27, 30, 31},
4068      kArm64S16x8UnzipRight},
4069     {{0, 1, 16, 17, 4, 5, 20, 21, 8, 9, 24, 25, 12, 13, 28, 29},
4070      kArm64S16x8TransposeLeft},
4071     {{2, 3, 18, 19, 6, 7, 22, 23, 10, 11, 26, 27, 14, 15, 30, 31},
4072      kArm64S16x8TransposeRight},
4073     {{6, 7, 4, 5, 2, 3, 0, 1, 14, 15, 12, 13, 10, 11, 8, 9},
4074      kArm64S16x4Reverse},
4075     {{2, 3, 0, 1, 6, 7, 4, 5, 10, 11, 8, 9, 14, 15, 12, 13},
4076      kArm64S16x2Reverse},
4077 
4078     {{0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23},
4079      kArm64S8x16ZipLeft},
4080     {{8, 24, 9, 25, 10, 26, 11, 27, 12, 28, 13, 29, 14, 30, 15, 31},
4081      kArm64S8x16ZipRight},
4082     {{0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30},
4083      kArm64S8x16UnzipLeft},
4084     {{1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31},
4085      kArm64S8x16UnzipRight},
4086     {{0, 16, 2, 18, 4, 20, 6, 22, 8, 24, 10, 26, 12, 28, 14, 30},
4087      kArm64S8x16TransposeLeft},
4088     {{1, 17, 3, 19, 5, 21, 7, 23, 9, 25, 11, 27, 13, 29, 15, 31},
4089      kArm64S8x16TransposeRight},
4090     {{7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8}, kArm64S8x8Reverse},
4091     {{3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12}, kArm64S8x4Reverse},
4092     {{1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14},
4093      kArm64S8x2Reverse}};
4094 
TryMatchArchShuffle(const uint8_t * shuffle,const ShuffleEntry * table,size_t num_entries,bool is_swizzle,ArchOpcode * opcode)4095 bool TryMatchArchShuffle(const uint8_t* shuffle, const ShuffleEntry* table,
4096                          size_t num_entries, bool is_swizzle,
4097                          ArchOpcode* opcode) {
4098   uint8_t mask = is_swizzle ? kSimd128Size - 1 : 2 * kSimd128Size - 1;
4099   for (size_t i = 0; i < num_entries; i++) {
4100     const ShuffleEntry& entry = table[i];
4101     int j = 0;
4102     for (; j < kSimd128Size; j++) {
4103       if ((entry.shuffle[j] & mask) != (shuffle[j] & mask)) {
4104         break;
4105       }
4106     }
4107     if (j == kSimd128Size) {
4108       *opcode = entry.opcode;
4109       return true;
4110     }
4111   }
4112   return false;
4113 }
4114 
ArrangeShuffleTable(Arm64OperandGenerator * g,Node * input0,Node * input1,InstructionOperand * src0,InstructionOperand * src1)4115 void ArrangeShuffleTable(Arm64OperandGenerator* g, Node* input0, Node* input1,
4116                          InstructionOperand* src0, InstructionOperand* src1) {
4117   if (input0 == input1) {
4118     // Unary, any q-register can be the table.
4119     *src0 = *src1 = g->UseRegister(input0);
4120   } else {
4121     // Binary, table registers must be consecutive.
4122     *src0 = g->UseFixed(input0, fp_fixed1);
4123     *src1 = g->UseFixed(input1, fp_fixed2);
4124   }
4125 }
4126 
4127 }  // namespace
4128 
VisitI8x16Shuffle(Node * node)4129 void InstructionSelector::VisitI8x16Shuffle(Node* node) {
4130   uint8_t shuffle[kSimd128Size];
4131   bool is_swizzle;
4132   CanonicalizeShuffle(node, shuffle, &is_swizzle);
4133   uint8_t shuffle32x4[4];
4134   Arm64OperandGenerator g(this);
4135   ArchOpcode opcode;
4136   if (TryMatchArchShuffle(shuffle, arch_shuffles, arraysize(arch_shuffles),
4137                           is_swizzle, &opcode)) {
4138     VisitRRR(this, opcode, node);
4139     return;
4140   }
4141   Node* input0 = node->InputAt(0);
4142   Node* input1 = node->InputAt(1);
4143   uint8_t offset;
4144   if (wasm::SimdShuffle::TryMatchConcat(shuffle, &offset)) {
4145     Emit(kArm64S8x16Concat, g.DefineAsRegister(node), g.UseRegister(input0),
4146          g.UseRegister(input1), g.UseImmediate(offset));
4147     return;
4148   }
4149   int index = 0;
4150   if (wasm::SimdShuffle::TryMatch32x4Shuffle(shuffle, shuffle32x4)) {
4151     if (wasm::SimdShuffle::TryMatchSplat<4>(shuffle, &index)) {
4152       DCHECK_GT(4, index);
4153       Emit(kArm64S128Dup, g.DefineAsRegister(node), g.UseRegister(input0),
4154            g.UseImmediate(4), g.UseImmediate(index % 4));
4155     } else if (wasm::SimdShuffle::TryMatchIdentity(shuffle)) {
4156       EmitIdentity(node);
4157     } else {
4158       Emit(kArm64S32x4Shuffle, g.DefineAsRegister(node), g.UseRegister(input0),
4159            g.UseRegister(input1),
4160            g.UseImmediate(wasm::SimdShuffle::Pack4Lanes(shuffle32x4)));
4161     }
4162     return;
4163   }
4164   if (wasm::SimdShuffle::TryMatchSplat<8>(shuffle, &index)) {
4165     DCHECK_GT(8, index);
4166     Emit(kArm64S128Dup, g.DefineAsRegister(node), g.UseRegister(input0),
4167          g.UseImmediate(8), g.UseImmediate(index % 8));
4168     return;
4169   }
4170   if (wasm::SimdShuffle::TryMatchSplat<16>(shuffle, &index)) {
4171     DCHECK_GT(16, index);
4172     Emit(kArm64S128Dup, g.DefineAsRegister(node), g.UseRegister(input0),
4173          g.UseImmediate(16), g.UseImmediate(index % 16));
4174     return;
4175   }
4176   // Code generator uses vtbl, arrange sources to form a valid lookup table.
4177   InstructionOperand src0, src1;
4178   ArrangeShuffleTable(&g, input0, input1, &src0, &src1);
4179   Emit(kArm64I8x16Shuffle, g.DefineAsRegister(node), src0, src1,
4180        g.UseImmediate(wasm::SimdShuffle::Pack4Lanes(shuffle)),
4181        g.UseImmediate(wasm::SimdShuffle::Pack4Lanes(shuffle + 4)),
4182        g.UseImmediate(wasm::SimdShuffle::Pack4Lanes(shuffle + 8)),
4183        g.UseImmediate(wasm::SimdShuffle::Pack4Lanes(shuffle + 12)));
4184 }
4185 #else
4186 void InstructionSelector::VisitI8x16Shuffle(Node* node) { UNREACHABLE(); }
4187 #endif  // V8_ENABLE_WEBASSEMBLY
4188 
VisitSignExtendWord8ToInt32(Node * node)4189 void InstructionSelector::VisitSignExtendWord8ToInt32(Node* node) {
4190   VisitRR(this, kArm64Sxtb32, node);
4191 }
4192 
VisitSignExtendWord16ToInt32(Node * node)4193 void InstructionSelector::VisitSignExtendWord16ToInt32(Node* node) {
4194   VisitRR(this, kArm64Sxth32, node);
4195 }
4196 
VisitSignExtendWord8ToInt64(Node * node)4197 void InstructionSelector::VisitSignExtendWord8ToInt64(Node* node) {
4198   VisitRR(this, kArm64Sxtb, node);
4199 }
4200 
VisitSignExtendWord16ToInt64(Node * node)4201 void InstructionSelector::VisitSignExtendWord16ToInt64(Node* node) {
4202   VisitRR(this, kArm64Sxth, node);
4203 }
4204 
VisitSignExtendWord32ToInt64(Node * node)4205 void InstructionSelector::VisitSignExtendWord32ToInt64(Node* node) {
4206   VisitRR(this, kArm64Sxtw, node);
4207 }
4208 
4209 namespace {
VisitPminOrPmax(InstructionSelector * selector,ArchOpcode opcode,Node * node)4210 void VisitPminOrPmax(InstructionSelector* selector, ArchOpcode opcode,
4211                      Node* node) {
4212   Arm64OperandGenerator g(selector);
4213   // Need all unique registers because we first compare the two inputs, then we
4214   // need the inputs to remain unchanged for the bitselect later.
4215   selector->Emit(opcode, g.DefineAsRegister(node),
4216                  g.UseUniqueRegister(node->InputAt(0)),
4217                  g.UseUniqueRegister(node->InputAt(1)));
4218 }
4219 }  // namespace
4220 
VisitF32x4Pmin(Node * node)4221 void InstructionSelector::VisitF32x4Pmin(Node* node) {
4222   VisitPminOrPmax(this, kArm64F32x4Pmin, node);
4223 }
4224 
VisitF32x4Pmax(Node * node)4225 void InstructionSelector::VisitF32x4Pmax(Node* node) {
4226   VisitPminOrPmax(this, kArm64F32x4Pmax, node);
4227 }
4228 
VisitF64x2Pmin(Node * node)4229 void InstructionSelector::VisitF64x2Pmin(Node* node) {
4230   VisitPminOrPmax(this, kArm64F64x2Pmin, node);
4231 }
4232 
VisitF64x2Pmax(Node * node)4233 void InstructionSelector::VisitF64x2Pmax(Node* node) {
4234   VisitPminOrPmax(this, kArm64F64x2Pmax, node);
4235 }
4236 
4237 namespace {
VisitSignExtendLong(InstructionSelector * selector,ArchOpcode opcode,Node * node,int lane_size)4238 void VisitSignExtendLong(InstructionSelector* selector, ArchOpcode opcode,
4239                          Node* node, int lane_size) {
4240   InstructionCode code = opcode;
4241   code |= LaneSizeField::encode(lane_size);
4242   VisitRR(selector, code, node);
4243 }
4244 }  // namespace
4245 
VisitI64x2SConvertI32x4Low(Node * node)4246 void InstructionSelector::VisitI64x2SConvertI32x4Low(Node* node) {
4247   VisitSignExtendLong(this, kArm64Sxtl, node, 64);
4248 }
4249 
VisitI64x2SConvertI32x4High(Node * node)4250 void InstructionSelector::VisitI64x2SConvertI32x4High(Node* node) {
4251   VisitSignExtendLong(this, kArm64Sxtl2, node, 64);
4252 }
4253 
VisitI64x2UConvertI32x4Low(Node * node)4254 void InstructionSelector::VisitI64x2UConvertI32x4Low(Node* node) {
4255   VisitSignExtendLong(this, kArm64Uxtl, node, 64);
4256 }
4257 
VisitI64x2UConvertI32x4High(Node * node)4258 void InstructionSelector::VisitI64x2UConvertI32x4High(Node* node) {
4259   VisitSignExtendLong(this, kArm64Uxtl2, node, 64);
4260 }
4261 
VisitI32x4SConvertI16x8Low(Node * node)4262 void InstructionSelector::VisitI32x4SConvertI16x8Low(Node* node) {
4263   VisitSignExtendLong(this, kArm64Sxtl, node, 32);
4264 }
4265 
VisitI32x4SConvertI16x8High(Node * node)4266 void InstructionSelector::VisitI32x4SConvertI16x8High(Node* node) {
4267   VisitSignExtendLong(this, kArm64Sxtl2, node, 32);
4268 }
4269 
VisitI32x4UConvertI16x8Low(Node * node)4270 void InstructionSelector::VisitI32x4UConvertI16x8Low(Node* node) {
4271   VisitSignExtendLong(this, kArm64Uxtl, node, 32);
4272 }
4273 
VisitI32x4UConvertI16x8High(Node * node)4274 void InstructionSelector::VisitI32x4UConvertI16x8High(Node* node) {
4275   VisitSignExtendLong(this, kArm64Uxtl2, node, 32);
4276 }
4277 
VisitI16x8SConvertI8x16Low(Node * node)4278 void InstructionSelector::VisitI16x8SConvertI8x16Low(Node* node) {
4279   VisitSignExtendLong(this, kArm64Sxtl, node, 16);
4280 }
4281 
VisitI16x8SConvertI8x16High(Node * node)4282 void InstructionSelector::VisitI16x8SConvertI8x16High(Node* node) {
4283   VisitSignExtendLong(this, kArm64Sxtl2, node, 16);
4284 }
4285 
VisitI16x8UConvertI8x16Low(Node * node)4286 void InstructionSelector::VisitI16x8UConvertI8x16Low(Node* node) {
4287   VisitSignExtendLong(this, kArm64Uxtl, node, 16);
4288 }
4289 
VisitI16x8UConvertI8x16High(Node * node)4290 void InstructionSelector::VisitI16x8UConvertI8x16High(Node* node) {
4291   VisitSignExtendLong(this, kArm64Uxtl2, node, 16);
4292 }
4293 
VisitI8x16Popcnt(Node * node)4294 void InstructionSelector::VisitI8x16Popcnt(Node* node) {
4295   InstructionCode code = kArm64Cnt;
4296   code |= LaneSizeField::encode(8);
4297   VisitRR(this, code, node);
4298 }
4299 
AddOutputToSelectContinuation(OperandGenerator * g,int first_input_index,Node * node)4300 void InstructionSelector::AddOutputToSelectContinuation(OperandGenerator* g,
4301                                                         int first_input_index,
4302                                                         Node* node) {
4303   continuation_outputs_.push_back(g->DefineAsRegister(node));
4304 }
4305 
4306 // static
4307 MachineOperatorBuilder::Flags
SupportedMachineOperatorFlags()4308 InstructionSelector::SupportedMachineOperatorFlags() {
4309   return MachineOperatorBuilder::kFloat32RoundDown |
4310          MachineOperatorBuilder::kFloat64RoundDown |
4311          MachineOperatorBuilder::kFloat32RoundUp |
4312          MachineOperatorBuilder::kFloat64RoundUp |
4313          MachineOperatorBuilder::kFloat32RoundTruncate |
4314          MachineOperatorBuilder::kFloat64RoundTruncate |
4315          MachineOperatorBuilder::kFloat64RoundTiesAway |
4316          MachineOperatorBuilder::kFloat32RoundTiesEven |
4317          MachineOperatorBuilder::kFloat64RoundTiesEven |
4318          MachineOperatorBuilder::kWord32Popcnt |
4319          MachineOperatorBuilder::kWord64Popcnt |
4320          MachineOperatorBuilder::kWord32ShiftIsSafe |
4321          MachineOperatorBuilder::kInt32DivIsSafe |
4322          MachineOperatorBuilder::kUint32DivIsSafe |
4323          MachineOperatorBuilder::kWord32ReverseBits |
4324          MachineOperatorBuilder::kWord64ReverseBits |
4325          MachineOperatorBuilder::kSatConversionIsSafe |
4326          MachineOperatorBuilder::kFloat32Select |
4327          MachineOperatorBuilder::kFloat64Select;
4328 }
4329 
4330 // static
4331 MachineOperatorBuilder::AlignmentRequirements
AlignmentRequirements()4332 InstructionSelector::AlignmentRequirements() {
4333   return MachineOperatorBuilder::AlignmentRequirements::
4334       FullUnalignedAccessSupport();
4335 }
4336 
4337 }  // namespace compiler
4338 }  // namespace internal
4339 }  // namespace v8
4340