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