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/assembler-inl.h"
6 #include "src/compiler/instruction-selector-impl.h"
7 #include "src/compiler/node-matchers.h"
8 #include "src/compiler/node-properties.h"
9
10 namespace v8 {
11 namespace internal {
12 namespace compiler {
13
14 enum ImmediateMode {
15 kArithmeticImm, // 12 bit unsigned immediate shifted left 0 or 12 bits
16 kShift32Imm, // 0 - 31
17 kShift64Imm, // 0 - 63
18 kLogical32Imm,
19 kLogical64Imm,
20 kLoadStoreImm8, // signed 8 bit or 12 bit unsigned scaled by access size
21 kLoadStoreImm16,
22 kLoadStoreImm32,
23 kLoadStoreImm64,
24 kNoImmediate
25 };
26
27
28 // Adds Arm64-specific methods for generating operands.
29 class Arm64OperandGenerator final : public OperandGenerator {
30 public:
Arm64OperandGenerator(InstructionSelector * selector)31 explicit Arm64OperandGenerator(InstructionSelector* selector)
32 : OperandGenerator(selector) {}
33
UseOperand(Node * node,ImmediateMode mode)34 InstructionOperand UseOperand(Node* node, ImmediateMode mode) {
35 if (CanBeImmediate(node, mode)) {
36 return UseImmediate(node);
37 }
38 return UseRegister(node);
39 }
40
41 // Use the zero register if the node has the immediate value zero, otherwise
42 // assign a register.
UseRegisterOrImmediateZero(Node * node)43 InstructionOperand UseRegisterOrImmediateZero(Node* node) {
44 if ((IsIntegerConstant(node) && (GetIntegerConstantValue(node) == 0)) ||
45 (IsFloatConstant(node) &&
46 (bit_cast<int64_t>(GetFloatConstantValue(node)) == 0))) {
47 return UseImmediate(node);
48 }
49 return UseRegister(node);
50 }
51
52 // Use the stack pointer if the node is LoadStackPointer, otherwise assign a
53 // register.
UseRegisterOrStackPointer(Node * node,bool sp_allowed)54 InstructionOperand UseRegisterOrStackPointer(Node* node, bool sp_allowed) {
55 if (sp_allowed && node->opcode() == IrOpcode::kLoadStackPointer)
56 return LocationOperand(LocationOperand::EXPLICIT,
57 LocationOperand::REGISTER,
58 MachineRepresentation::kWord64, sp.code());
59 return UseRegister(node);
60 }
61
62 // Use the provided node if it has the required value, or create a
63 // TempImmediate otherwise.
UseImmediateOrTemp(Node * node,int32_t value)64 InstructionOperand UseImmediateOrTemp(Node* node, int32_t value) {
65 if (GetIntegerConstantValue(node) == value) {
66 return UseImmediate(node);
67 }
68 return TempImmediate(value);
69 }
70
IsIntegerConstant(Node * node)71 bool IsIntegerConstant(Node* node) {
72 return (node->opcode() == IrOpcode::kInt32Constant) ||
73 (node->opcode() == IrOpcode::kInt64Constant);
74 }
75
GetIntegerConstantValue(Node * node)76 int64_t GetIntegerConstantValue(Node* node) {
77 if (node->opcode() == IrOpcode::kInt32Constant) {
78 return OpParameter<int32_t>(node->op());
79 }
80 DCHECK_EQ(IrOpcode::kInt64Constant, node->opcode());
81 return OpParameter<int64_t>(node->op());
82 }
83
IsFloatConstant(Node * node)84 bool IsFloatConstant(Node* node) {
85 return (node->opcode() == IrOpcode::kFloat32Constant) ||
86 (node->opcode() == IrOpcode::kFloat64Constant);
87 }
88
GetFloatConstantValue(Node * node)89 double GetFloatConstantValue(Node* node) {
90 if (node->opcode() == IrOpcode::kFloat32Constant) {
91 return OpParameter<float>(node->op());
92 }
93 DCHECK_EQ(IrOpcode::kFloat64Constant, node->opcode());
94 return OpParameter<double>(node->op());
95 }
96
CanBeImmediate(Node * node,ImmediateMode mode)97 bool CanBeImmediate(Node* node, ImmediateMode mode) {
98 return IsIntegerConstant(node) &&
99 CanBeImmediate(GetIntegerConstantValue(node), mode);
100 }
101
CanBeImmediate(int64_t value,ImmediateMode mode)102 bool CanBeImmediate(int64_t value, ImmediateMode mode) {
103 unsigned ignored;
104 switch (mode) {
105 case kLogical32Imm:
106 // TODO(dcarney): some unencodable values can be handled by
107 // switching instructions.
108 return Assembler::IsImmLogical(static_cast<uint64_t>(value), 32,
109 &ignored, &ignored, &ignored);
110 case kLogical64Imm:
111 return Assembler::IsImmLogical(static_cast<uint64_t>(value), 64,
112 &ignored, &ignored, &ignored);
113 case kArithmeticImm:
114 return Assembler::IsImmAddSub(value);
115 case kLoadStoreImm8:
116 return IsLoadStoreImmediate(value, 0);
117 case kLoadStoreImm16:
118 return IsLoadStoreImmediate(value, 1);
119 case kLoadStoreImm32:
120 return IsLoadStoreImmediate(value, 2);
121 case kLoadStoreImm64:
122 return IsLoadStoreImmediate(value, 3);
123 case kNoImmediate:
124 return false;
125 case kShift32Imm: // Fall through.
126 case kShift64Imm:
127 // Shift operations only observe the bottom 5 or 6 bits of the value.
128 // All possible shifts can be encoded by discarding bits which have no
129 // effect.
130 return true;
131 }
132 return false;
133 }
134
CanBeLoadStoreShiftImmediate(Node * node,MachineRepresentation rep)135 bool CanBeLoadStoreShiftImmediate(Node* node, MachineRepresentation rep) {
136 // TODO(arm64): Load and Store on 128 bit Q registers is not supported yet.
137 DCHECK_GT(MachineRepresentation::kSimd128, rep);
138 return IsIntegerConstant(node) &&
139 (GetIntegerConstantValue(node) == ElementSizeLog2Of(rep));
140 }
141
142 private:
IsLoadStoreImmediate(int64_t value,unsigned size)143 bool IsLoadStoreImmediate(int64_t value, unsigned size) {
144 return Assembler::IsImmLSScaled(value, size) ||
145 Assembler::IsImmLSUnscaled(value);
146 }
147 };
148
149
150 namespace {
151
VisitRR(InstructionSelector * selector,ArchOpcode opcode,Node * node)152 void VisitRR(InstructionSelector* selector, ArchOpcode opcode, Node* node) {
153 Arm64OperandGenerator g(selector);
154 selector->Emit(opcode, g.DefineAsRegister(node),
155 g.UseRegister(node->InputAt(0)));
156 }
157
158
VisitRRR(InstructionSelector * selector,ArchOpcode opcode,Node * node)159 void VisitRRR(InstructionSelector* selector, ArchOpcode opcode, Node* node) {
160 Arm64OperandGenerator g(selector);
161 selector->Emit(opcode, g.DefineAsRegister(node),
162 g.UseRegister(node->InputAt(0)),
163 g.UseRegister(node->InputAt(1)));
164 }
165
VisitRRI(InstructionSelector * selector,ArchOpcode opcode,Node * node)166 void VisitRRI(InstructionSelector* selector, ArchOpcode opcode, Node* node) {
167 Arm64OperandGenerator g(selector);
168 int32_t imm = OpParameter<int32_t>(node->op());
169 selector->Emit(opcode, g.DefineAsRegister(node),
170 g.UseRegister(node->InputAt(0)), g.UseImmediate(imm));
171 }
172
VisitRRO(InstructionSelector * selector,ArchOpcode opcode,Node * node,ImmediateMode operand_mode)173 void VisitRRO(InstructionSelector* selector, ArchOpcode opcode, Node* node,
174 ImmediateMode operand_mode) {
175 Arm64OperandGenerator g(selector);
176 selector->Emit(opcode, g.DefineAsRegister(node),
177 g.UseRegister(node->InputAt(0)),
178 g.UseOperand(node->InputAt(1), operand_mode));
179 }
180
VisitRRIR(InstructionSelector * selector,ArchOpcode opcode,Node * node)181 void VisitRRIR(InstructionSelector* selector, ArchOpcode opcode, Node* node) {
182 Arm64OperandGenerator g(selector);
183 int32_t imm = OpParameter<int32_t>(node->op());
184 selector->Emit(opcode, g.DefineAsRegister(node),
185 g.UseRegister(node->InputAt(0)), g.UseImmediate(imm),
186 g.UseRegister(node->InputAt(1)));
187 }
188
189 struct ExtendingLoadMatcher {
ExtendingLoadMatcherv8::internal::compiler::__anon47426b030111::ExtendingLoadMatcher190 ExtendingLoadMatcher(Node* node, InstructionSelector* selector)
191 : matches_(false), selector_(selector), base_(nullptr), immediate_(0) {
192 Initialize(node);
193 }
194
Matchesv8::internal::compiler::__anon47426b030111::ExtendingLoadMatcher195 bool Matches() const { return matches_; }
196
basev8::internal::compiler::__anon47426b030111::ExtendingLoadMatcher197 Node* base() const {
198 DCHECK(Matches());
199 return base_;
200 }
immediatev8::internal::compiler::__anon47426b030111::ExtendingLoadMatcher201 int64_t immediate() const {
202 DCHECK(Matches());
203 return immediate_;
204 }
opcodev8::internal::compiler::__anon47426b030111::ExtendingLoadMatcher205 ArchOpcode opcode() const {
206 DCHECK(Matches());
207 return opcode_;
208 }
209
210 private:
211 bool matches_;
212 InstructionSelector* selector_;
213 Node* base_;
214 int64_t immediate_;
215 ArchOpcode opcode_;
216
Initializev8::internal::compiler::__anon47426b030111::ExtendingLoadMatcher217 void Initialize(Node* node) {
218 Int64BinopMatcher m(node);
219 // When loading a 64-bit value and shifting by 32, we should
220 // just load and sign-extend the interesting 4 bytes instead.
221 // This happens, for example, when we're loading and untagging SMIs.
222 DCHECK(m.IsWord64Sar());
223 if (m.left().IsLoad() && m.right().Is(32) &&
224 selector_->CanCover(m.node(), m.left().node())) {
225 Arm64OperandGenerator g(selector_);
226 Node* load = m.left().node();
227 Node* offset = load->InputAt(1);
228 base_ = load->InputAt(0);
229 opcode_ = kArm64Ldrsw;
230 if (g.IsIntegerConstant(offset)) {
231 immediate_ = g.GetIntegerConstantValue(offset) + 4;
232 matches_ = g.CanBeImmediate(immediate_, kLoadStoreImm32);
233 }
234 }
235 }
236 };
237
TryMatchExtendingLoad(InstructionSelector * selector,Node * node)238 bool TryMatchExtendingLoad(InstructionSelector* selector, Node* node) {
239 ExtendingLoadMatcher m(node, selector);
240 return m.Matches();
241 }
242
TryEmitExtendingLoad(InstructionSelector * selector,Node * node)243 bool TryEmitExtendingLoad(InstructionSelector* selector, Node* node) {
244 ExtendingLoadMatcher m(node, selector);
245 Arm64OperandGenerator g(selector);
246 if (m.Matches()) {
247 InstructionOperand inputs[2];
248 inputs[0] = g.UseRegister(m.base());
249 InstructionCode opcode =
250 m.opcode() | AddressingModeField::encode(kMode_MRI);
251 DCHECK(is_int32(m.immediate()));
252 inputs[1] = g.TempImmediate(static_cast<int32_t>(m.immediate()));
253 InstructionOperand outputs[] = {g.DefineAsRegister(node)};
254 selector->Emit(opcode, arraysize(outputs), outputs, arraysize(inputs),
255 inputs);
256 return true;
257 }
258 return false;
259 }
260
TryMatchAnyShift(InstructionSelector * selector,Node * node,Node * input_node,InstructionCode * opcode,bool try_ror)261 bool TryMatchAnyShift(InstructionSelector* selector, Node* node,
262 Node* input_node, InstructionCode* opcode, bool try_ror) {
263 Arm64OperandGenerator g(selector);
264
265 if (!selector->CanCover(node, input_node)) return false;
266 if (input_node->InputCount() != 2) return false;
267 if (!g.IsIntegerConstant(input_node->InputAt(1))) return false;
268
269 switch (input_node->opcode()) {
270 case IrOpcode::kWord32Shl:
271 case IrOpcode::kWord64Shl:
272 *opcode |= AddressingModeField::encode(kMode_Operand2_R_LSL_I);
273 return true;
274 case IrOpcode::kWord32Shr:
275 case IrOpcode::kWord64Shr:
276 *opcode |= AddressingModeField::encode(kMode_Operand2_R_LSR_I);
277 return true;
278 case IrOpcode::kWord32Sar:
279 *opcode |= AddressingModeField::encode(kMode_Operand2_R_ASR_I);
280 return true;
281 case IrOpcode::kWord64Sar:
282 if (TryMatchExtendingLoad(selector, input_node)) return false;
283 *opcode |= AddressingModeField::encode(kMode_Operand2_R_ASR_I);
284 return true;
285 case IrOpcode::kWord32Ror:
286 case IrOpcode::kWord64Ror:
287 if (try_ror) {
288 *opcode |= AddressingModeField::encode(kMode_Operand2_R_ROR_I);
289 return true;
290 }
291 return false;
292 default:
293 return false;
294 }
295 }
296
297
TryMatchAnyExtend(Arm64OperandGenerator * g,InstructionSelector * selector,Node * node,Node * left_node,Node * right_node,InstructionOperand * left_op,InstructionOperand * right_op,InstructionCode * opcode)298 bool TryMatchAnyExtend(Arm64OperandGenerator* g, InstructionSelector* selector,
299 Node* node, Node* left_node, Node* right_node,
300 InstructionOperand* left_op,
301 InstructionOperand* right_op, InstructionCode* opcode) {
302 if (!selector->CanCover(node, right_node)) return false;
303
304 NodeMatcher nm(right_node);
305
306 if (nm.IsWord32And()) {
307 Int32BinopMatcher mright(right_node);
308 if (mright.right().Is(0xFF) || mright.right().Is(0xFFFF)) {
309 int32_t mask = mright.right().Value();
310 *left_op = g->UseRegister(left_node);
311 *right_op = g->UseRegister(mright.left().node());
312 *opcode |= AddressingModeField::encode(
313 (mask == 0xFF) ? kMode_Operand2_R_UXTB : kMode_Operand2_R_UXTH);
314 return true;
315 }
316 } else if (nm.IsWord32Sar()) {
317 Int32BinopMatcher mright(right_node);
318 if (selector->CanCover(mright.node(), mright.left().node()) &&
319 mright.left().IsWord32Shl()) {
320 Int32BinopMatcher mleft_of_right(mright.left().node());
321 if ((mright.right().Is(16) && mleft_of_right.right().Is(16)) ||
322 (mright.right().Is(24) && mleft_of_right.right().Is(24))) {
323 int32_t shift = mright.right().Value();
324 *left_op = g->UseRegister(left_node);
325 *right_op = g->UseRegister(mleft_of_right.left().node());
326 *opcode |= AddressingModeField::encode(
327 (shift == 24) ? kMode_Operand2_R_SXTB : kMode_Operand2_R_SXTH);
328 return true;
329 }
330 }
331 }
332 return false;
333 }
334
TryMatchLoadStoreShift(Arm64OperandGenerator * g,InstructionSelector * selector,MachineRepresentation rep,Node * node,Node * index,InstructionOperand * index_op,InstructionOperand * shift_immediate_op)335 bool TryMatchLoadStoreShift(Arm64OperandGenerator* g,
336 InstructionSelector* selector,
337 MachineRepresentation rep, Node* node, Node* index,
338 InstructionOperand* index_op,
339 InstructionOperand* shift_immediate_op) {
340 if (!selector->CanCover(node, index)) return false;
341 if (index->InputCount() != 2) return false;
342 Node* left = index->InputAt(0);
343 Node* right = index->InputAt(1);
344 switch (index->opcode()) {
345 case IrOpcode::kWord32Shl:
346 case IrOpcode::kWord64Shl:
347 if (!g->CanBeLoadStoreShiftImmediate(right, rep)) {
348 return false;
349 }
350 *index_op = g->UseRegister(left);
351 *shift_immediate_op = g->UseImmediate(right);
352 return true;
353 default:
354 return false;
355 }
356 }
357
358 // Bitfields describing binary operator properties:
359 // CanCommuteField is true if we can switch the two operands, potentially
360 // requiring commuting the flags continuation condition.
361 typedef BitField8<bool, 1, 1> CanCommuteField;
362 // MustCommuteCondField is true when we need to commute the flags continuation
363 // condition in order to switch the operands.
364 typedef BitField8<bool, 2, 1> MustCommuteCondField;
365 // IsComparisonField is true when the operation is a comparison and has no other
366 // result other than the condition.
367 typedef BitField8<bool, 3, 1> IsComparisonField;
368 // IsAddSubField is true when an instruction is encoded as ADD or SUB.
369 typedef BitField8<bool, 4, 1> IsAddSubField;
370
371 // Get properties of a binary operator.
GetBinopProperties(InstructionCode opcode)372 uint8_t GetBinopProperties(InstructionCode opcode) {
373 uint8_t result = 0;
374 switch (opcode) {
375 case kArm64Cmp32:
376 case kArm64Cmp:
377 // We can commute CMP by switching the inputs and commuting
378 // the flags continuation.
379 result = CanCommuteField::update(result, true);
380 result = MustCommuteCondField::update(result, true);
381 result = IsComparisonField::update(result, true);
382 // The CMP and CMN instructions are encoded as SUB or ADD
383 // with zero output register, and therefore support the same
384 // operand modes.
385 result = IsAddSubField::update(result, true);
386 break;
387 case kArm64Cmn32:
388 case kArm64Cmn:
389 result = CanCommuteField::update(result, true);
390 result = IsComparisonField::update(result, true);
391 result = IsAddSubField::update(result, true);
392 break;
393 case kArm64Add32:
394 case kArm64Add:
395 result = CanCommuteField::update(result, true);
396 result = IsAddSubField::update(result, true);
397 break;
398 case kArm64Sub32:
399 case kArm64Sub:
400 result = IsAddSubField::update(result, true);
401 break;
402 case kArm64Tst32:
403 case kArm64Tst:
404 result = CanCommuteField::update(result, true);
405 result = IsComparisonField::update(result, true);
406 break;
407 case kArm64And32:
408 case kArm64And:
409 case kArm64Or32:
410 case kArm64Or:
411 case kArm64Eor32:
412 case kArm64Eor:
413 result = CanCommuteField::update(result, true);
414 break;
415 default:
416 UNREACHABLE();
417 }
418 DCHECK_IMPLIES(MustCommuteCondField::decode(result),
419 CanCommuteField::decode(result));
420 return result;
421 }
422
423 // Shared routine for multiple binary operations.
424 template <typename Matcher>
VisitBinop(InstructionSelector * selector,Node * node,InstructionCode opcode,ImmediateMode operand_mode,FlagsContinuation * cont)425 void VisitBinop(InstructionSelector* selector, Node* node,
426 InstructionCode opcode, ImmediateMode operand_mode,
427 FlagsContinuation* cont) {
428 Arm64OperandGenerator g(selector);
429 InstructionOperand inputs[3];
430 size_t input_count = 0;
431 InstructionOperand outputs[1];
432 size_t output_count = 0;
433
434 Node* left_node = node->InputAt(0);
435 Node* right_node = node->InputAt(1);
436
437 uint8_t properties = GetBinopProperties(opcode);
438 bool can_commute = CanCommuteField::decode(properties);
439 bool must_commute_cond = MustCommuteCondField::decode(properties);
440 bool is_add_sub = IsAddSubField::decode(properties);
441
442 if (g.CanBeImmediate(right_node, operand_mode)) {
443 inputs[input_count++] = g.UseRegister(left_node);
444 inputs[input_count++] = g.UseImmediate(right_node);
445 } else if (can_commute && g.CanBeImmediate(left_node, operand_mode)) {
446 if (must_commute_cond) cont->Commute();
447 inputs[input_count++] = g.UseRegister(right_node);
448 inputs[input_count++] = g.UseImmediate(left_node);
449 } else if (is_add_sub &&
450 TryMatchAnyExtend(&g, selector, node, left_node, right_node,
451 &inputs[0], &inputs[1], &opcode)) {
452 input_count += 2;
453 } else if (is_add_sub && can_commute &&
454 TryMatchAnyExtend(&g, selector, node, right_node, left_node,
455 &inputs[0], &inputs[1], &opcode)) {
456 if (must_commute_cond) cont->Commute();
457 input_count += 2;
458 } else if (TryMatchAnyShift(selector, node, right_node, &opcode,
459 !is_add_sub)) {
460 Matcher m_shift(right_node);
461 inputs[input_count++] = g.UseRegisterOrImmediateZero(left_node);
462 inputs[input_count++] = g.UseRegister(m_shift.left().node());
463 // We only need at most the last 6 bits of the shift.
464 inputs[input_count++] =
465 g.UseImmediate(static_cast<int>(m_shift.right().Value() & 0x3F));
466 } else if (can_commute && TryMatchAnyShift(selector, node, left_node, &opcode,
467 !is_add_sub)) {
468 if (must_commute_cond) cont->Commute();
469 Matcher m_shift(left_node);
470 inputs[input_count++] = g.UseRegisterOrImmediateZero(right_node);
471 inputs[input_count++] = g.UseRegister(m_shift.left().node());
472 // We only need at most the last 6 bits of the shift.
473 inputs[input_count++] =
474 g.UseImmediate(static_cast<int>(m_shift.right().Value() & 0x3F));
475 } else {
476 inputs[input_count++] = g.UseRegisterOrImmediateZero(left_node);
477 inputs[input_count++] = g.UseRegister(right_node);
478 }
479
480 if (!IsComparisonField::decode(properties)) {
481 outputs[output_count++] = g.DefineAsRegister(node);
482 }
483
484 DCHECK_NE(0u, input_count);
485 DCHECK((output_count != 0) || IsComparisonField::decode(properties));
486 DCHECK_GE(arraysize(inputs), input_count);
487 DCHECK_GE(arraysize(outputs), output_count);
488
489 selector->EmitWithContinuation(opcode, output_count, outputs, input_count,
490 inputs, cont);
491 }
492
493
494 // Shared routine for multiple binary operations.
495 template <typename Matcher>
VisitBinop(InstructionSelector * selector,Node * node,ArchOpcode opcode,ImmediateMode operand_mode)496 void VisitBinop(InstructionSelector* selector, Node* node, ArchOpcode opcode,
497 ImmediateMode operand_mode) {
498 FlagsContinuation cont;
499 VisitBinop<Matcher>(selector, node, opcode, operand_mode, &cont);
500 }
501
502
503 template <typename Matcher>
VisitAddSub(InstructionSelector * selector,Node * node,ArchOpcode opcode,ArchOpcode negate_opcode)504 void VisitAddSub(InstructionSelector* selector, Node* node, ArchOpcode opcode,
505 ArchOpcode negate_opcode) {
506 Arm64OperandGenerator g(selector);
507 Matcher m(node);
508 if (m.right().HasValue() && (m.right().Value() < 0) &&
509 g.CanBeImmediate(-m.right().Value(), kArithmeticImm)) {
510 selector->Emit(negate_opcode, g.DefineAsRegister(node),
511 g.UseRegister(m.left().node()),
512 g.TempImmediate(static_cast<int32_t>(-m.right().Value())));
513 } else {
514 VisitBinop<Matcher>(selector, node, opcode, kArithmeticImm);
515 }
516 }
517
518
519 // For multiplications by immediate of the form x * (2^k + 1), where k > 0,
520 // return the value of k, otherwise return zero. This is used to reduce the
521 // multiplication to addition with left shift: x + (x << k).
522 template <typename Matcher>
LeftShiftForReducedMultiply(Matcher * m)523 int32_t LeftShiftForReducedMultiply(Matcher* m) {
524 DCHECK(m->IsInt32Mul() || m->IsInt64Mul());
525 if (m->right().HasValue() && m->right().Value() >= 3) {
526 uint64_t value_minus_one = m->right().Value() - 1;
527 if (base::bits::IsPowerOfTwo(value_minus_one)) {
528 return WhichPowerOf2(value_minus_one);
529 }
530 }
531 return 0;
532 }
533
534 } // namespace
535
VisitStackSlot(Node * node)536 void InstructionSelector::VisitStackSlot(Node* node) {
537 StackSlotRepresentation rep = StackSlotRepresentationOf(node->op());
538 int slot = frame_->AllocateSpillSlot(rep.size());
539 OperandGenerator g(this);
540
541 Emit(kArchStackSlot, g.DefineAsRegister(node),
542 sequence()->AddImmediate(Constant(slot)), 0, nullptr);
543 }
544
VisitDebugAbort(Node * node)545 void InstructionSelector::VisitDebugAbort(Node* node) {
546 Arm64OperandGenerator g(this);
547 Emit(kArchDebugAbort, g.NoOutput(), g.UseFixed(node->InputAt(0), x1));
548 }
549
EmitLoad(InstructionSelector * selector,Node * node,InstructionCode opcode,ImmediateMode immediate_mode,MachineRepresentation rep,Node * output=nullptr)550 void EmitLoad(InstructionSelector* selector, Node* node, InstructionCode opcode,
551 ImmediateMode immediate_mode, MachineRepresentation rep,
552 Node* output = nullptr) {
553 Arm64OperandGenerator g(selector);
554 Node* base = node->InputAt(0);
555 Node* index = node->InputAt(1);
556 InstructionOperand inputs[3];
557 size_t input_count = 0;
558 InstructionOperand outputs[1];
559
560 // If output is not nullptr, use that as the output register. This
561 // is used when we merge a conversion into the load.
562 outputs[0] = g.DefineAsRegister(output == nullptr ? node : output);
563
564 if (selector->CanAddressRelativeToRootsRegister()) {
565 ExternalReferenceMatcher m(base);
566 if (m.HasValue() && g.IsIntegerConstant(index)) {
567 ptrdiff_t const delta =
568 g.GetIntegerConstantValue(index) +
569 TurboAssemblerBase::RootRegisterOffsetForExternalReference(
570 selector->isolate(), m.Value());
571 input_count = 1;
572 // Check that the delta is a 32-bit integer due to the limitations of
573 // immediate operands.
574 if (is_int32(delta)) {
575 inputs[0] = g.UseImmediate(static_cast<int32_t>(delta));
576 opcode |= AddressingModeField::encode(kMode_Root);
577 selector->Emit(opcode, arraysize(outputs), outputs, input_count,
578 inputs);
579 return;
580 }
581 }
582 }
583
584 inputs[0] = g.UseRegister(base);
585
586 if (g.CanBeImmediate(index, immediate_mode)) {
587 input_count = 2;
588 inputs[1] = g.UseImmediate(index);
589 opcode |= AddressingModeField::encode(kMode_MRI);
590 } else if (TryMatchLoadStoreShift(&g, selector, rep, node, index, &inputs[1],
591 &inputs[2])) {
592 input_count = 3;
593 opcode |= AddressingModeField::encode(kMode_Operand2_R_LSL_I);
594 } else {
595 input_count = 2;
596 inputs[1] = g.UseRegister(index);
597 opcode |= AddressingModeField::encode(kMode_MRR);
598 }
599
600 selector->Emit(opcode, arraysize(outputs), outputs, input_count, inputs);
601 }
602
VisitLoad(Node * node)603 void InstructionSelector::VisitLoad(Node* node) {
604 InstructionCode opcode = kArchNop;
605 ImmediateMode immediate_mode = kNoImmediate;
606 LoadRepresentation load_rep = LoadRepresentationOf(node->op());
607 MachineRepresentation rep = load_rep.representation();
608 switch (rep) {
609 case MachineRepresentation::kFloat32:
610 opcode = kArm64LdrS;
611 immediate_mode = kLoadStoreImm32;
612 break;
613 case MachineRepresentation::kFloat64:
614 opcode = kArm64LdrD;
615 immediate_mode = kLoadStoreImm64;
616 break;
617 case MachineRepresentation::kBit: // Fall through.
618 case MachineRepresentation::kWord8:
619 opcode = load_rep.IsSigned() ? kArm64Ldrsb : kArm64Ldrb;
620 immediate_mode = kLoadStoreImm8;
621 break;
622 case MachineRepresentation::kWord16:
623 opcode = load_rep.IsSigned() ? kArm64Ldrsh : kArm64Ldrh;
624 immediate_mode = kLoadStoreImm16;
625 break;
626 case MachineRepresentation::kWord32:
627 opcode = kArm64LdrW;
628 immediate_mode = kLoadStoreImm32;
629 break;
630 case MachineRepresentation::kTaggedSigned: // Fall through.
631 case MachineRepresentation::kTaggedPointer: // Fall through.
632 case MachineRepresentation::kTagged: // Fall through.
633 case MachineRepresentation::kWord64:
634 opcode = kArm64Ldr;
635 immediate_mode = kLoadStoreImm64;
636 break;
637 case MachineRepresentation::kSimd128:
638 opcode = kArm64LdrQ;
639 immediate_mode = kNoImmediate;
640 break;
641 case MachineRepresentation::kNone:
642 UNREACHABLE();
643 return;
644 }
645 if (node->opcode() == IrOpcode::kPoisonedLoad) {
646 CHECK_NE(poisoning_level_, PoisoningMitigationLevel::kDontPoison);
647 opcode |= MiscField::encode(kMemoryAccessPoisoned);
648 }
649
650 EmitLoad(this, node, opcode, immediate_mode, rep);
651 }
652
VisitPoisonedLoad(Node * node)653 void InstructionSelector::VisitPoisonedLoad(Node* node) { VisitLoad(node); }
654
VisitProtectedLoad(Node * node)655 void InstructionSelector::VisitProtectedLoad(Node* node) {
656 // TODO(eholk)
657 UNIMPLEMENTED();
658 }
659
VisitStore(Node * node)660 void InstructionSelector::VisitStore(Node* node) {
661 Arm64OperandGenerator g(this);
662 Node* base = node->InputAt(0);
663 Node* index = node->InputAt(1);
664 Node* value = node->InputAt(2);
665
666 StoreRepresentation store_rep = StoreRepresentationOf(node->op());
667 WriteBarrierKind write_barrier_kind = store_rep.write_barrier_kind();
668 MachineRepresentation rep = store_rep.representation();
669
670 // TODO(arm64): I guess this could be done in a better way.
671 if (write_barrier_kind != kNoWriteBarrier) {
672 DCHECK(CanBeTaggedPointer(rep));
673 AddressingMode addressing_mode;
674 InstructionOperand inputs[3];
675 size_t input_count = 0;
676 inputs[input_count++] = g.UseUniqueRegister(base);
677 // OutOfLineRecordWrite uses the index in an arithmetic instruction, so we
678 // must check kArithmeticImm as well as kLoadStoreImm64.
679 if (g.CanBeImmediate(index, kArithmeticImm) &&
680 g.CanBeImmediate(index, kLoadStoreImm64)) {
681 inputs[input_count++] = g.UseImmediate(index);
682 addressing_mode = kMode_MRI;
683 } else {
684 inputs[input_count++] = g.UseUniqueRegister(index);
685 addressing_mode = kMode_MRR;
686 }
687 inputs[input_count++] = g.UseUniqueRegister(value);
688 RecordWriteMode record_write_mode = RecordWriteMode::kValueIsAny;
689 switch (write_barrier_kind) {
690 case kNoWriteBarrier:
691 UNREACHABLE();
692 break;
693 case kMapWriteBarrier:
694 record_write_mode = RecordWriteMode::kValueIsMap;
695 break;
696 case kPointerWriteBarrier:
697 record_write_mode = RecordWriteMode::kValueIsPointer;
698 break;
699 case kFullWriteBarrier:
700 record_write_mode = RecordWriteMode::kValueIsAny;
701 break;
702 }
703 InstructionOperand temps[] = {g.TempRegister(), g.TempRegister()};
704 size_t const temp_count = arraysize(temps);
705 InstructionCode code = kArchStoreWithWriteBarrier;
706 code |= AddressingModeField::encode(addressing_mode);
707 code |= MiscField::encode(static_cast<int>(record_write_mode));
708 Emit(code, 0, nullptr, input_count, inputs, temp_count, temps);
709 } else {
710 InstructionOperand inputs[4];
711 size_t input_count = 0;
712 InstructionCode opcode = kArchNop;
713 ImmediateMode immediate_mode = kNoImmediate;
714 switch (rep) {
715 case MachineRepresentation::kFloat32:
716 opcode = kArm64StrS;
717 immediate_mode = kLoadStoreImm32;
718 break;
719 case MachineRepresentation::kFloat64:
720 opcode = kArm64StrD;
721 immediate_mode = kLoadStoreImm64;
722 break;
723 case MachineRepresentation::kBit: // Fall through.
724 case MachineRepresentation::kWord8:
725 opcode = kArm64Strb;
726 immediate_mode = kLoadStoreImm8;
727 break;
728 case MachineRepresentation::kWord16:
729 opcode = kArm64Strh;
730 immediate_mode = kLoadStoreImm16;
731 break;
732 case MachineRepresentation::kWord32:
733 opcode = kArm64StrW;
734 immediate_mode = kLoadStoreImm32;
735 break;
736 case MachineRepresentation::kTaggedSigned: // Fall through.
737 case MachineRepresentation::kTaggedPointer: // Fall through.
738 case MachineRepresentation::kTagged: // Fall through.
739 case MachineRepresentation::kWord64:
740 opcode = kArm64Str;
741 immediate_mode = kLoadStoreImm64;
742 break;
743 case MachineRepresentation::kSimd128:
744 opcode = kArm64StrQ;
745 immediate_mode = kNoImmediate;
746 break;
747 case MachineRepresentation::kNone:
748 UNREACHABLE();
749 return;
750 }
751
752 inputs[0] = g.UseRegisterOrImmediateZero(value);
753 inputs[1] = g.UseRegister(base);
754
755 if (g.CanBeImmediate(index, immediate_mode)) {
756 input_count = 3;
757 inputs[2] = g.UseImmediate(index);
758 opcode |= AddressingModeField::encode(kMode_MRI);
759 } else if (TryMatchLoadStoreShift(&g, this, rep, node, index, &inputs[2],
760 &inputs[3])) {
761 input_count = 4;
762 opcode |= AddressingModeField::encode(kMode_Operand2_R_LSL_I);
763 } else {
764 input_count = 3;
765 inputs[2] = g.UseRegister(index);
766 opcode |= AddressingModeField::encode(kMode_MRR);
767 }
768
769 Emit(opcode, 0, nullptr, input_count, inputs);
770 }
771 }
772
VisitProtectedStore(Node * node)773 void InstructionSelector::VisitProtectedStore(Node* node) {
774 // TODO(eholk)
775 UNIMPLEMENTED();
776 }
777
778 // Architecture supports unaligned access, therefore VisitLoad is used instead
VisitUnalignedLoad(Node * node)779 void InstructionSelector::VisitUnalignedLoad(Node* node) { UNREACHABLE(); }
780
781 // Architecture supports unaligned access, therefore VisitStore is used instead
VisitUnalignedStore(Node * node)782 void InstructionSelector::VisitUnalignedStore(Node* node) { UNREACHABLE(); }
783
784 template <typename Matcher>
VisitLogical(InstructionSelector * selector,Node * node,Matcher * m,ArchOpcode opcode,bool left_can_cover,bool right_can_cover,ImmediateMode imm_mode)785 static void VisitLogical(InstructionSelector* selector, Node* node, Matcher* m,
786 ArchOpcode opcode, bool left_can_cover,
787 bool right_can_cover, ImmediateMode imm_mode) {
788 Arm64OperandGenerator g(selector);
789
790 // Map instruction to equivalent operation with inverted right input.
791 ArchOpcode inv_opcode = opcode;
792 switch (opcode) {
793 case kArm64And32:
794 inv_opcode = kArm64Bic32;
795 break;
796 case kArm64And:
797 inv_opcode = kArm64Bic;
798 break;
799 case kArm64Or32:
800 inv_opcode = kArm64Orn32;
801 break;
802 case kArm64Or:
803 inv_opcode = kArm64Orn;
804 break;
805 case kArm64Eor32:
806 inv_opcode = kArm64Eon32;
807 break;
808 case kArm64Eor:
809 inv_opcode = kArm64Eon;
810 break;
811 default:
812 UNREACHABLE();
813 }
814
815 // Select Logical(y, ~x) for Logical(Xor(x, -1), y).
816 if ((m->left().IsWord32Xor() || m->left().IsWord64Xor()) && left_can_cover) {
817 Matcher mleft(m->left().node());
818 if (mleft.right().Is(-1)) {
819 // TODO(all): support shifted operand on right.
820 selector->Emit(inv_opcode, g.DefineAsRegister(node),
821 g.UseRegister(m->right().node()),
822 g.UseRegister(mleft.left().node()));
823 return;
824 }
825 }
826
827 // Select Logical(x, ~y) for Logical(x, Xor(y, -1)).
828 if ((m->right().IsWord32Xor() || m->right().IsWord64Xor()) &&
829 right_can_cover) {
830 Matcher mright(m->right().node());
831 if (mright.right().Is(-1)) {
832 // TODO(all): support shifted operand on right.
833 selector->Emit(inv_opcode, g.DefineAsRegister(node),
834 g.UseRegister(m->left().node()),
835 g.UseRegister(mright.left().node()));
836 return;
837 }
838 }
839
840 if (m->IsWord32Xor() && m->right().Is(-1)) {
841 selector->Emit(kArm64Not32, g.DefineAsRegister(node),
842 g.UseRegister(m->left().node()));
843 } else if (m->IsWord64Xor() && m->right().Is(-1)) {
844 selector->Emit(kArm64Not, g.DefineAsRegister(node),
845 g.UseRegister(m->left().node()));
846 } else {
847 VisitBinop<Matcher>(selector, node, opcode, imm_mode);
848 }
849 }
850
851
VisitWord32And(Node * node)852 void InstructionSelector::VisitWord32And(Node* node) {
853 Arm64OperandGenerator g(this);
854 Int32BinopMatcher m(node);
855 if (m.left().IsWord32Shr() && CanCover(node, m.left().node()) &&
856 m.right().HasValue()) {
857 uint32_t mask = m.right().Value();
858 uint32_t mask_width = base::bits::CountPopulation(mask);
859 uint32_t mask_msb = base::bits::CountLeadingZeros32(mask);
860 if ((mask_width != 0) && (mask_width != 32) &&
861 (mask_msb + mask_width == 32)) {
862 // The mask must be contiguous, and occupy the least-significant bits.
863 DCHECK_EQ(0u, base::bits::CountTrailingZeros32(mask));
864
865 // Select Ubfx for And(Shr(x, imm), mask) where the mask is in the least
866 // significant bits.
867 Int32BinopMatcher mleft(m.left().node());
868 if (mleft.right().HasValue()) {
869 // Any shift value can match; int32 shifts use `value % 32`.
870 uint32_t lsb = mleft.right().Value() & 0x1F;
871
872 // Ubfx cannot extract bits past the register size, however since
873 // shifting the original value would have introduced some zeros we can
874 // still use ubfx with a smaller mask and the remaining bits will be
875 // zeros.
876 if (lsb + mask_width > 32) mask_width = 32 - lsb;
877
878 Emit(kArm64Ubfx32, g.DefineAsRegister(node),
879 g.UseRegister(mleft.left().node()),
880 g.UseImmediateOrTemp(mleft.right().node(), lsb),
881 g.TempImmediate(mask_width));
882 return;
883 }
884 // Other cases fall through to the normal And operation.
885 }
886 }
887 VisitLogical<Int32BinopMatcher>(
888 this, node, &m, kArm64And32, CanCover(node, m.left().node()),
889 CanCover(node, m.right().node()), kLogical32Imm);
890 }
891
892
VisitWord64And(Node * node)893 void InstructionSelector::VisitWord64And(Node* node) {
894 Arm64OperandGenerator g(this);
895 Int64BinopMatcher m(node);
896 if (m.left().IsWord64Shr() && CanCover(node, m.left().node()) &&
897 m.right().HasValue()) {
898 uint64_t mask = m.right().Value();
899 uint64_t mask_width = base::bits::CountPopulation(mask);
900 uint64_t mask_msb = base::bits::CountLeadingZeros64(mask);
901 if ((mask_width != 0) && (mask_width != 64) &&
902 (mask_msb + mask_width == 64)) {
903 // The mask must be contiguous, and occupy the least-significant bits.
904 DCHECK_EQ(0u, base::bits::CountTrailingZeros64(mask));
905
906 // Select Ubfx for And(Shr(x, imm), mask) where the mask is in the least
907 // significant bits.
908 Int64BinopMatcher mleft(m.left().node());
909 if (mleft.right().HasValue()) {
910 // Any shift value can match; int64 shifts use `value % 64`.
911 uint32_t lsb = static_cast<uint32_t>(mleft.right().Value() & 0x3F);
912
913 // Ubfx cannot extract bits past the register size, however since
914 // shifting the original value would have introduced some zeros we can
915 // still use ubfx with a smaller mask and the remaining bits will be
916 // zeros.
917 if (lsb + mask_width > 64) mask_width = 64 - lsb;
918
919 Emit(kArm64Ubfx, g.DefineAsRegister(node),
920 g.UseRegister(mleft.left().node()),
921 g.UseImmediateOrTemp(mleft.right().node(), lsb),
922 g.TempImmediate(static_cast<int32_t>(mask_width)));
923 return;
924 }
925 // Other cases fall through to the normal And operation.
926 }
927 }
928 VisitLogical<Int64BinopMatcher>(
929 this, node, &m, kArm64And, CanCover(node, m.left().node()),
930 CanCover(node, m.right().node()), kLogical64Imm);
931 }
932
933
VisitWord32Or(Node * node)934 void InstructionSelector::VisitWord32Or(Node* node) {
935 Int32BinopMatcher m(node);
936 VisitLogical<Int32BinopMatcher>(
937 this, node, &m, kArm64Or32, CanCover(node, m.left().node()),
938 CanCover(node, m.right().node()), kLogical32Imm);
939 }
940
941
VisitWord64Or(Node * node)942 void InstructionSelector::VisitWord64Or(Node* node) {
943 Int64BinopMatcher m(node);
944 VisitLogical<Int64BinopMatcher>(
945 this, node, &m, kArm64Or, CanCover(node, m.left().node()),
946 CanCover(node, m.right().node()), kLogical64Imm);
947 }
948
949
VisitWord32Xor(Node * node)950 void InstructionSelector::VisitWord32Xor(Node* node) {
951 Int32BinopMatcher m(node);
952 VisitLogical<Int32BinopMatcher>(
953 this, node, &m, kArm64Eor32, CanCover(node, m.left().node()),
954 CanCover(node, m.right().node()), kLogical32Imm);
955 }
956
957
VisitWord64Xor(Node * node)958 void InstructionSelector::VisitWord64Xor(Node* node) {
959 Int64BinopMatcher m(node);
960 VisitLogical<Int64BinopMatcher>(
961 this, node, &m, kArm64Eor, CanCover(node, m.left().node()),
962 CanCover(node, m.right().node()), kLogical64Imm);
963 }
964
965
VisitWord32Shl(Node * node)966 void InstructionSelector::VisitWord32Shl(Node* node) {
967 Int32BinopMatcher m(node);
968 if (m.left().IsWord32And() && CanCover(node, m.left().node()) &&
969 m.right().IsInRange(1, 31)) {
970 Arm64OperandGenerator g(this);
971 Int32BinopMatcher mleft(m.left().node());
972 if (mleft.right().HasValue()) {
973 uint32_t mask = mleft.right().Value();
974 uint32_t mask_width = base::bits::CountPopulation(mask);
975 uint32_t mask_msb = base::bits::CountLeadingZeros32(mask);
976 if ((mask_width != 0) && (mask_msb + mask_width == 32)) {
977 uint32_t shift = m.right().Value();
978 DCHECK_EQ(0u, base::bits::CountTrailingZeros32(mask));
979 DCHECK_NE(0u, shift);
980
981 if ((shift + mask_width) >= 32) {
982 // If the mask is contiguous and reaches or extends beyond the top
983 // bit, only the shift is needed.
984 Emit(kArm64Lsl32, g.DefineAsRegister(node),
985 g.UseRegister(mleft.left().node()),
986 g.UseImmediate(m.right().node()));
987 return;
988 } else {
989 // Select Ubfiz for Shl(And(x, mask), imm) where the mask is
990 // contiguous, and the shift immediate non-zero.
991 Emit(kArm64Ubfiz32, g.DefineAsRegister(node),
992 g.UseRegister(mleft.left().node()),
993 g.UseImmediate(m.right().node()), g.TempImmediate(mask_width));
994 return;
995 }
996 }
997 }
998 }
999 VisitRRO(this, kArm64Lsl32, node, kShift32Imm);
1000 }
1001
1002
VisitWord64Shl(Node * node)1003 void InstructionSelector::VisitWord64Shl(Node* node) {
1004 Arm64OperandGenerator g(this);
1005 Int64BinopMatcher m(node);
1006 if ((m.left().IsChangeInt32ToInt64() || m.left().IsChangeUint32ToUint64()) &&
1007 m.right().IsInRange(32, 63) && CanCover(node, m.left().node())) {
1008 // There's no need to sign/zero-extend to 64-bit if we shift out the upper
1009 // 32 bits anyway.
1010 Emit(kArm64Lsl, g.DefineAsRegister(node),
1011 g.UseRegister(m.left().node()->InputAt(0)),
1012 g.UseImmediate(m.right().node()));
1013 return;
1014 }
1015 VisitRRO(this, kArm64Lsl, node, kShift64Imm);
1016 }
1017
1018
1019 namespace {
1020
TryEmitBitfieldExtract32(InstructionSelector * selector,Node * node)1021 bool TryEmitBitfieldExtract32(InstructionSelector* selector, Node* node) {
1022 Arm64OperandGenerator g(selector);
1023 Int32BinopMatcher m(node);
1024 if (selector->CanCover(node, m.left().node()) && m.left().IsWord32Shl()) {
1025 // Select Ubfx or Sbfx for (x << (K & 0x1F)) OP (K & 0x1F), where
1026 // OP is >>> or >> and (K & 0x1F) != 0.
1027 Int32BinopMatcher mleft(m.left().node());
1028 if (mleft.right().HasValue() && m.right().HasValue() &&
1029 (mleft.right().Value() & 0x1F) != 0 &&
1030 (mleft.right().Value() & 0x1F) == (m.right().Value() & 0x1F)) {
1031 DCHECK(m.IsWord32Shr() || m.IsWord32Sar());
1032 ArchOpcode opcode = m.IsWord32Sar() ? kArm64Sbfx32 : kArm64Ubfx32;
1033
1034 int right_val = m.right().Value() & 0x1F;
1035 DCHECK_NE(right_val, 0);
1036
1037 selector->Emit(opcode, g.DefineAsRegister(node),
1038 g.UseRegister(mleft.left().node()), g.TempImmediate(0),
1039 g.TempImmediate(32 - right_val));
1040 return true;
1041 }
1042 }
1043 return false;
1044 }
1045
1046 } // namespace
1047
1048
VisitWord32Shr(Node * node)1049 void InstructionSelector::VisitWord32Shr(Node* node) {
1050 Int32BinopMatcher m(node);
1051 if (m.left().IsWord32And() && m.right().HasValue()) {
1052 uint32_t lsb = m.right().Value() & 0x1F;
1053 Int32BinopMatcher mleft(m.left().node());
1054 if (mleft.right().HasValue() && mleft.right().Value() != 0) {
1055 // Select Ubfx for Shr(And(x, mask), imm) where the result of the mask is
1056 // shifted into the least-significant bits.
1057 uint32_t mask = (mleft.right().Value() >> lsb) << lsb;
1058 unsigned mask_width = base::bits::CountPopulation(mask);
1059 unsigned mask_msb = base::bits::CountLeadingZeros32(mask);
1060 if ((mask_msb + mask_width + lsb) == 32) {
1061 Arm64OperandGenerator g(this);
1062 DCHECK_EQ(lsb, base::bits::CountTrailingZeros32(mask));
1063 Emit(kArm64Ubfx32, g.DefineAsRegister(node),
1064 g.UseRegister(mleft.left().node()),
1065 g.UseImmediateOrTemp(m.right().node(), lsb),
1066 g.TempImmediate(mask_width));
1067 return;
1068 }
1069 }
1070 } else if (TryEmitBitfieldExtract32(this, node)) {
1071 return;
1072 }
1073
1074 if (m.left().IsUint32MulHigh() && m.right().HasValue() &&
1075 CanCover(node, node->InputAt(0))) {
1076 // Combine this shift with the multiply and shift that would be generated
1077 // by Uint32MulHigh.
1078 Arm64OperandGenerator g(this);
1079 Node* left = m.left().node();
1080 int shift = m.right().Value() & 0x1F;
1081 InstructionOperand const smull_operand = g.TempRegister();
1082 Emit(kArm64Umull, smull_operand, g.UseRegister(left->InputAt(0)),
1083 g.UseRegister(left->InputAt(1)));
1084 Emit(kArm64Lsr, g.DefineAsRegister(node), smull_operand,
1085 g.TempImmediate(32 + shift));
1086 return;
1087 }
1088
1089 VisitRRO(this, kArm64Lsr32, node, kShift32Imm);
1090 }
1091
1092
VisitWord64Shr(Node * node)1093 void InstructionSelector::VisitWord64Shr(Node* node) {
1094 Int64BinopMatcher m(node);
1095 if (m.left().IsWord64And() && m.right().HasValue()) {
1096 uint32_t lsb = m.right().Value() & 0x3F;
1097 Int64BinopMatcher mleft(m.left().node());
1098 if (mleft.right().HasValue() && mleft.right().Value() != 0) {
1099 // Select Ubfx for Shr(And(x, mask), imm) where the result of the mask is
1100 // shifted into the least-significant bits.
1101 uint64_t mask = (mleft.right().Value() >> lsb) << lsb;
1102 unsigned mask_width = base::bits::CountPopulation(mask);
1103 unsigned mask_msb = base::bits::CountLeadingZeros64(mask);
1104 if ((mask_msb + mask_width + lsb) == 64) {
1105 Arm64OperandGenerator g(this);
1106 DCHECK_EQ(lsb, base::bits::CountTrailingZeros64(mask));
1107 Emit(kArm64Ubfx, g.DefineAsRegister(node),
1108 g.UseRegister(mleft.left().node()),
1109 g.UseImmediateOrTemp(m.right().node(), lsb),
1110 g.TempImmediate(mask_width));
1111 return;
1112 }
1113 }
1114 }
1115 VisitRRO(this, kArm64Lsr, node, kShift64Imm);
1116 }
1117
1118
VisitWord32Sar(Node * node)1119 void InstructionSelector::VisitWord32Sar(Node* node) {
1120 if (TryEmitBitfieldExtract32(this, node)) {
1121 return;
1122 }
1123
1124 Int32BinopMatcher m(node);
1125 if (m.left().IsInt32MulHigh() && m.right().HasValue() &&
1126 CanCover(node, node->InputAt(0))) {
1127 // Combine this shift with the multiply and shift that would be generated
1128 // by Int32MulHigh.
1129 Arm64OperandGenerator g(this);
1130 Node* left = m.left().node();
1131 int shift = m.right().Value() & 0x1F;
1132 InstructionOperand const smull_operand = g.TempRegister();
1133 Emit(kArm64Smull, smull_operand, g.UseRegister(left->InputAt(0)),
1134 g.UseRegister(left->InputAt(1)));
1135 Emit(kArm64Asr, g.DefineAsRegister(node), smull_operand,
1136 g.TempImmediate(32 + shift));
1137 return;
1138 }
1139
1140 if (m.left().IsInt32Add() && m.right().HasValue() &&
1141 CanCover(node, node->InputAt(0))) {
1142 Node* add_node = m.left().node();
1143 Int32BinopMatcher madd_node(add_node);
1144 if (madd_node.left().IsInt32MulHigh() &&
1145 CanCover(add_node, madd_node.left().node())) {
1146 // Combine the shift that would be generated by Int32MulHigh with the add
1147 // on the left of this Sar operation. We do it here, as the result of the
1148 // add potentially has 33 bits, so we have to ensure the result is
1149 // truncated by being the input to this 32-bit Sar operation.
1150 Arm64OperandGenerator g(this);
1151 Node* mul_node = madd_node.left().node();
1152
1153 InstructionOperand const smull_operand = g.TempRegister();
1154 Emit(kArm64Smull, smull_operand, g.UseRegister(mul_node->InputAt(0)),
1155 g.UseRegister(mul_node->InputAt(1)));
1156
1157 InstructionOperand const add_operand = g.TempRegister();
1158 Emit(kArm64Add | AddressingModeField::encode(kMode_Operand2_R_ASR_I),
1159 add_operand, g.UseRegister(add_node->InputAt(1)), smull_operand,
1160 g.TempImmediate(32));
1161
1162 Emit(kArm64Asr32, g.DefineAsRegister(node), add_operand,
1163 g.UseImmediate(node->InputAt(1)));
1164 return;
1165 }
1166 }
1167
1168 VisitRRO(this, kArm64Asr32, node, kShift32Imm);
1169 }
1170
1171
VisitWord64Sar(Node * node)1172 void InstructionSelector::VisitWord64Sar(Node* node) {
1173 if (TryEmitExtendingLoad(this, node)) return;
1174 VisitRRO(this, kArm64Asr, node, kShift64Imm);
1175 }
1176
1177
VisitWord32Ror(Node * node)1178 void InstructionSelector::VisitWord32Ror(Node* node) {
1179 VisitRRO(this, kArm64Ror32, node, kShift32Imm);
1180 }
1181
1182
VisitWord64Ror(Node * node)1183 void InstructionSelector::VisitWord64Ror(Node* node) {
1184 VisitRRO(this, kArm64Ror, node, kShift64Imm);
1185 }
1186
1187 #define RR_OP_LIST(V) \
1188 V(Word64Clz, kArm64Clz) \
1189 V(Word32Clz, kArm64Clz32) \
1190 V(Word32ReverseBits, kArm64Rbit32) \
1191 V(Word64ReverseBits, kArm64Rbit) \
1192 V(Word32ReverseBytes, kArm64Rev32) \
1193 V(Word64ReverseBytes, kArm64Rev) \
1194 V(ChangeFloat32ToFloat64, kArm64Float32ToFloat64) \
1195 V(RoundInt32ToFloat32, kArm64Int32ToFloat32) \
1196 V(RoundUint32ToFloat32, kArm64Uint32ToFloat32) \
1197 V(ChangeInt32ToFloat64, kArm64Int32ToFloat64) \
1198 V(ChangeUint32ToFloat64, kArm64Uint32ToFloat64) \
1199 V(TruncateFloat32ToInt32, kArm64Float32ToInt32) \
1200 V(ChangeFloat64ToInt32, kArm64Float64ToInt32) \
1201 V(TruncateFloat32ToUint32, kArm64Float32ToUint32) \
1202 V(ChangeFloat64ToUint32, kArm64Float64ToUint32) \
1203 V(ChangeFloat64ToUint64, kArm64Float64ToUint64) \
1204 V(TruncateFloat64ToUint32, kArm64Float64ToUint32) \
1205 V(TruncateFloat64ToFloat32, kArm64Float64ToFloat32) \
1206 V(TruncateFloat64ToWord32, kArchTruncateDoubleToI) \
1207 V(RoundFloat64ToInt32, kArm64Float64ToInt32) \
1208 V(RoundInt64ToFloat32, kArm64Int64ToFloat32) \
1209 V(RoundInt64ToFloat64, kArm64Int64ToFloat64) \
1210 V(RoundUint64ToFloat32, kArm64Uint64ToFloat32) \
1211 V(RoundUint64ToFloat64, kArm64Uint64ToFloat64) \
1212 V(BitcastFloat32ToInt32, kArm64Float64ExtractLowWord32) \
1213 V(BitcastFloat64ToInt64, kArm64U64MoveFloat64) \
1214 V(BitcastInt32ToFloat32, kArm64Float64MoveU64) \
1215 V(BitcastInt64ToFloat64, kArm64Float64MoveU64) \
1216 V(Float32Abs, kArm64Float32Abs) \
1217 V(Float64Abs, kArm64Float64Abs) \
1218 V(Float32Sqrt, kArm64Float32Sqrt) \
1219 V(Float64Sqrt, kArm64Float64Sqrt) \
1220 V(Float32RoundDown, kArm64Float32RoundDown) \
1221 V(Float64RoundDown, kArm64Float64RoundDown) \
1222 V(Float32RoundUp, kArm64Float32RoundUp) \
1223 V(Float64RoundUp, kArm64Float64RoundUp) \
1224 V(Float32RoundTruncate, kArm64Float32RoundTruncate) \
1225 V(Float64RoundTruncate, kArm64Float64RoundTruncate) \
1226 V(Float64RoundTiesAway, kArm64Float64RoundTiesAway) \
1227 V(Float32RoundTiesEven, kArm64Float32RoundTiesEven) \
1228 V(Float64RoundTiesEven, kArm64Float64RoundTiesEven) \
1229 V(Float32Neg, kArm64Float32Neg) \
1230 V(Float64Neg, kArm64Float64Neg) \
1231 V(Float64ExtractLowWord32, kArm64Float64ExtractLowWord32) \
1232 V(Float64ExtractHighWord32, kArm64Float64ExtractHighWord32) \
1233 V(Float64SilenceNaN, kArm64Float64SilenceNaN)
1234
1235 #define RRR_OP_LIST(V) \
1236 V(Int32Div, kArm64Idiv32) \
1237 V(Int64Div, kArm64Idiv) \
1238 V(Uint32Div, kArm64Udiv32) \
1239 V(Uint64Div, kArm64Udiv) \
1240 V(Int32Mod, kArm64Imod32) \
1241 V(Int64Mod, kArm64Imod) \
1242 V(Uint32Mod, kArm64Umod32) \
1243 V(Uint64Mod, kArm64Umod) \
1244 V(Float32Add, kArm64Float32Add) \
1245 V(Float64Add, kArm64Float64Add) \
1246 V(Float32Sub, kArm64Float32Sub) \
1247 V(Float64Sub, kArm64Float64Sub) \
1248 V(Float32Mul, kArm64Float32Mul) \
1249 V(Float64Mul, kArm64Float64Mul) \
1250 V(Float32Div, kArm64Float32Div) \
1251 V(Float64Div, kArm64Float64Div) \
1252 V(Float32Max, kArm64Float32Max) \
1253 V(Float64Max, kArm64Float64Max) \
1254 V(Float32Min, kArm64Float32Min) \
1255 V(Float64Min, kArm64Float64Min)
1256
1257 #define RR_VISITOR(Name, opcode) \
1258 void InstructionSelector::Visit##Name(Node* node) { \
1259 VisitRR(this, opcode, node); \
1260 }
1261 RR_OP_LIST(RR_VISITOR)
1262 #undef RR_VISITOR
1263 #undef RR_OP_LIST
1264
1265 #define RRR_VISITOR(Name, opcode) \
1266 void InstructionSelector::Visit##Name(Node* node) { \
1267 VisitRRR(this, opcode, node); \
1268 }
RRR_OP_LIST(RRR_VISITOR)1269 RRR_OP_LIST(RRR_VISITOR)
1270 #undef RRR_VISITOR
1271 #undef RRR_OP_LIST
1272
1273 void InstructionSelector::VisitWord32Ctz(Node* node) { UNREACHABLE(); }
1274
VisitWord64Ctz(Node * node)1275 void InstructionSelector::VisitWord64Ctz(Node* node) { UNREACHABLE(); }
1276
VisitWord32Popcnt(Node * node)1277 void InstructionSelector::VisitWord32Popcnt(Node* node) { UNREACHABLE(); }
1278
VisitWord64Popcnt(Node * node)1279 void InstructionSelector::VisitWord64Popcnt(Node* node) { UNREACHABLE(); }
1280
VisitSpeculationFence(Node * node)1281 void InstructionSelector::VisitSpeculationFence(Node* node) {
1282 Arm64OperandGenerator g(this);
1283 Emit(kArm64DsbIsb, g.NoOutput());
1284 }
1285
VisitInt32Add(Node * node)1286 void InstructionSelector::VisitInt32Add(Node* node) {
1287 Arm64OperandGenerator g(this);
1288 Int32BinopMatcher m(node);
1289 // Select Madd(x, y, z) for Add(Mul(x, y), z).
1290 if (m.left().IsInt32Mul() && CanCover(node, m.left().node())) {
1291 Int32BinopMatcher mleft(m.left().node());
1292 // Check multiply can't be later reduced to addition with shift.
1293 if (LeftShiftForReducedMultiply(&mleft) == 0) {
1294 Emit(kArm64Madd32, g.DefineAsRegister(node),
1295 g.UseRegister(mleft.left().node()),
1296 g.UseRegister(mleft.right().node()),
1297 g.UseRegister(m.right().node()));
1298 return;
1299 }
1300 }
1301 // Select Madd(x, y, z) for Add(z, Mul(x, y)).
1302 if (m.right().IsInt32Mul() && CanCover(node, m.right().node())) {
1303 Int32BinopMatcher mright(m.right().node());
1304 // Check multiply can't be later reduced to addition with shift.
1305 if (LeftShiftForReducedMultiply(&mright) == 0) {
1306 Emit(kArm64Madd32, g.DefineAsRegister(node),
1307 g.UseRegister(mright.left().node()),
1308 g.UseRegister(mright.right().node()),
1309 g.UseRegister(m.left().node()));
1310 return;
1311 }
1312 }
1313 VisitAddSub<Int32BinopMatcher>(this, node, kArm64Add32, kArm64Sub32);
1314 }
1315
1316
VisitInt64Add(Node * node)1317 void InstructionSelector::VisitInt64Add(Node* node) {
1318 Arm64OperandGenerator g(this);
1319 Int64BinopMatcher m(node);
1320 // Select Madd(x, y, z) for Add(Mul(x, y), z).
1321 if (m.left().IsInt64Mul() && CanCover(node, m.left().node())) {
1322 Int64BinopMatcher mleft(m.left().node());
1323 // Check multiply can't be later reduced to addition with shift.
1324 if (LeftShiftForReducedMultiply(&mleft) == 0) {
1325 Emit(kArm64Madd, g.DefineAsRegister(node),
1326 g.UseRegister(mleft.left().node()),
1327 g.UseRegister(mleft.right().node()),
1328 g.UseRegister(m.right().node()));
1329 return;
1330 }
1331 }
1332 // Select Madd(x, y, z) for Add(z, Mul(x, y)).
1333 if (m.right().IsInt64Mul() && CanCover(node, m.right().node())) {
1334 Int64BinopMatcher mright(m.right().node());
1335 // Check multiply can't be later reduced to addition with shift.
1336 if (LeftShiftForReducedMultiply(&mright) == 0) {
1337 Emit(kArm64Madd, g.DefineAsRegister(node),
1338 g.UseRegister(mright.left().node()),
1339 g.UseRegister(mright.right().node()),
1340 g.UseRegister(m.left().node()));
1341 return;
1342 }
1343 }
1344 VisitAddSub<Int64BinopMatcher>(this, node, kArm64Add, kArm64Sub);
1345 }
1346
1347
VisitInt32Sub(Node * node)1348 void InstructionSelector::VisitInt32Sub(Node* node) {
1349 Arm64OperandGenerator g(this);
1350 Int32BinopMatcher m(node);
1351
1352 // Select Msub(x, y, a) for Sub(a, Mul(x, y)).
1353 if (m.right().IsInt32Mul() && CanCover(node, m.right().node())) {
1354 Int32BinopMatcher mright(m.right().node());
1355 // Check multiply can't be later reduced to addition with shift.
1356 if (LeftShiftForReducedMultiply(&mright) == 0) {
1357 Emit(kArm64Msub32, g.DefineAsRegister(node),
1358 g.UseRegister(mright.left().node()),
1359 g.UseRegister(mright.right().node()),
1360 g.UseRegister(m.left().node()));
1361 return;
1362 }
1363 }
1364
1365 VisitAddSub<Int32BinopMatcher>(this, node, kArm64Sub32, kArm64Add32);
1366 }
1367
1368
VisitInt64Sub(Node * node)1369 void InstructionSelector::VisitInt64Sub(Node* node) {
1370 Arm64OperandGenerator g(this);
1371 Int64BinopMatcher m(node);
1372
1373 // Select Msub(x, y, a) for Sub(a, Mul(x, y)).
1374 if (m.right().IsInt64Mul() && CanCover(node, m.right().node())) {
1375 Int64BinopMatcher mright(m.right().node());
1376 // Check multiply can't be later reduced to addition with shift.
1377 if (LeftShiftForReducedMultiply(&mright) == 0) {
1378 Emit(kArm64Msub, g.DefineAsRegister(node),
1379 g.UseRegister(mright.left().node()),
1380 g.UseRegister(mright.right().node()),
1381 g.UseRegister(m.left().node()));
1382 return;
1383 }
1384 }
1385
1386 VisitAddSub<Int64BinopMatcher>(this, node, kArm64Sub, kArm64Add);
1387 }
1388
1389 namespace {
1390
EmitInt32MulWithOverflow(InstructionSelector * selector,Node * node,FlagsContinuation * cont)1391 void EmitInt32MulWithOverflow(InstructionSelector* selector, Node* node,
1392 FlagsContinuation* cont) {
1393 Arm64OperandGenerator g(selector);
1394 Int32BinopMatcher m(node);
1395 InstructionOperand result = g.DefineAsRegister(node);
1396 InstructionOperand left = g.UseRegister(m.left().node());
1397 InstructionOperand right = g.UseRegister(m.right().node());
1398 selector->Emit(kArm64Smull, result, left, right);
1399
1400 InstructionCode opcode =
1401 kArm64Cmp | AddressingModeField::encode(kMode_Operand2_R_SXTW);
1402 selector->EmitWithContinuation(opcode, result, result, cont);
1403 }
1404
1405 } // namespace
1406
VisitInt32Mul(Node * node)1407 void InstructionSelector::VisitInt32Mul(Node* node) {
1408 Arm64OperandGenerator g(this);
1409 Int32BinopMatcher m(node);
1410
1411 // First, try to reduce the multiplication to addition with left shift.
1412 // x * (2^k + 1) -> x + (x << k)
1413 int32_t shift = LeftShiftForReducedMultiply(&m);
1414 if (shift > 0) {
1415 Emit(kArm64Add32 | AddressingModeField::encode(kMode_Operand2_R_LSL_I),
1416 g.DefineAsRegister(node), g.UseRegister(m.left().node()),
1417 g.UseRegister(m.left().node()), g.TempImmediate(shift));
1418 return;
1419 }
1420
1421 if (m.left().IsInt32Sub() && CanCover(node, m.left().node())) {
1422 Int32BinopMatcher mleft(m.left().node());
1423
1424 // Select Mneg(x, y) for Mul(Sub(0, x), y).
1425 if (mleft.left().Is(0)) {
1426 Emit(kArm64Mneg32, g.DefineAsRegister(node),
1427 g.UseRegister(mleft.right().node()),
1428 g.UseRegister(m.right().node()));
1429 return;
1430 }
1431 }
1432
1433 if (m.right().IsInt32Sub() && CanCover(node, m.right().node())) {
1434 Int32BinopMatcher mright(m.right().node());
1435
1436 // Select Mneg(x, y) for Mul(x, Sub(0, y)).
1437 if (mright.left().Is(0)) {
1438 Emit(kArm64Mneg32, g.DefineAsRegister(node),
1439 g.UseRegister(m.left().node()),
1440 g.UseRegister(mright.right().node()));
1441 return;
1442 }
1443 }
1444
1445 VisitRRR(this, kArm64Mul32, node);
1446 }
1447
1448
VisitInt64Mul(Node * node)1449 void InstructionSelector::VisitInt64Mul(Node* node) {
1450 Arm64OperandGenerator g(this);
1451 Int64BinopMatcher m(node);
1452
1453 // First, try to reduce the multiplication to addition with left shift.
1454 // x * (2^k + 1) -> x + (x << k)
1455 int32_t shift = LeftShiftForReducedMultiply(&m);
1456 if (shift > 0) {
1457 Emit(kArm64Add | AddressingModeField::encode(kMode_Operand2_R_LSL_I),
1458 g.DefineAsRegister(node), g.UseRegister(m.left().node()),
1459 g.UseRegister(m.left().node()), g.TempImmediate(shift));
1460 return;
1461 }
1462
1463 if (m.left().IsInt64Sub() && CanCover(node, m.left().node())) {
1464 Int64BinopMatcher mleft(m.left().node());
1465
1466 // Select Mneg(x, y) for Mul(Sub(0, x), y).
1467 if (mleft.left().Is(0)) {
1468 Emit(kArm64Mneg, g.DefineAsRegister(node),
1469 g.UseRegister(mleft.right().node()),
1470 g.UseRegister(m.right().node()));
1471 return;
1472 }
1473 }
1474
1475 if (m.right().IsInt64Sub() && CanCover(node, m.right().node())) {
1476 Int64BinopMatcher mright(m.right().node());
1477
1478 // Select Mneg(x, y) for Mul(x, Sub(0, y)).
1479 if (mright.left().Is(0)) {
1480 Emit(kArm64Mneg, g.DefineAsRegister(node), g.UseRegister(m.left().node()),
1481 g.UseRegister(mright.right().node()));
1482 return;
1483 }
1484 }
1485
1486 VisitRRR(this, kArm64Mul, node);
1487 }
1488
VisitInt32MulHigh(Node * node)1489 void InstructionSelector::VisitInt32MulHigh(Node* node) {
1490 Arm64OperandGenerator g(this);
1491 InstructionOperand const smull_operand = g.TempRegister();
1492 Emit(kArm64Smull, smull_operand, g.UseRegister(node->InputAt(0)),
1493 g.UseRegister(node->InputAt(1)));
1494 Emit(kArm64Asr, g.DefineAsRegister(node), smull_operand, g.TempImmediate(32));
1495 }
1496
1497
VisitUint32MulHigh(Node * node)1498 void InstructionSelector::VisitUint32MulHigh(Node* node) {
1499 Arm64OperandGenerator g(this);
1500 InstructionOperand const smull_operand = g.TempRegister();
1501 Emit(kArm64Umull, smull_operand, g.UseRegister(node->InputAt(0)),
1502 g.UseRegister(node->InputAt(1)));
1503 Emit(kArm64Lsr, g.DefineAsRegister(node), smull_operand, g.TempImmediate(32));
1504 }
1505
1506
VisitTryTruncateFloat32ToInt64(Node * node)1507 void InstructionSelector::VisitTryTruncateFloat32ToInt64(Node* node) {
1508 Arm64OperandGenerator g(this);
1509
1510 InstructionOperand inputs[] = {g.UseRegister(node->InputAt(0))};
1511 InstructionOperand outputs[2];
1512 size_t output_count = 0;
1513 outputs[output_count++] = g.DefineAsRegister(node);
1514
1515 Node* success_output = NodeProperties::FindProjection(node, 1);
1516 if (success_output) {
1517 outputs[output_count++] = g.DefineAsRegister(success_output);
1518 }
1519
1520 Emit(kArm64Float32ToInt64, output_count, outputs, 1, inputs);
1521 }
1522
1523
VisitTryTruncateFloat64ToInt64(Node * node)1524 void InstructionSelector::VisitTryTruncateFloat64ToInt64(Node* node) {
1525 Arm64OperandGenerator g(this);
1526
1527 InstructionOperand inputs[] = {g.UseRegister(node->InputAt(0))};
1528 InstructionOperand outputs[2];
1529 size_t output_count = 0;
1530 outputs[output_count++] = g.DefineAsRegister(node);
1531
1532 Node* success_output = NodeProperties::FindProjection(node, 1);
1533 if (success_output) {
1534 outputs[output_count++] = g.DefineAsRegister(success_output);
1535 }
1536
1537 Emit(kArm64Float64ToInt64, output_count, outputs, 1, inputs);
1538 }
1539
1540
VisitTryTruncateFloat32ToUint64(Node * node)1541 void InstructionSelector::VisitTryTruncateFloat32ToUint64(Node* node) {
1542 Arm64OperandGenerator g(this);
1543
1544 InstructionOperand inputs[] = {g.UseRegister(node->InputAt(0))};
1545 InstructionOperand outputs[2];
1546 size_t output_count = 0;
1547 outputs[output_count++] = g.DefineAsRegister(node);
1548
1549 Node* success_output = NodeProperties::FindProjection(node, 1);
1550 if (success_output) {
1551 outputs[output_count++] = g.DefineAsRegister(success_output);
1552 }
1553
1554 Emit(kArm64Float32ToUint64, output_count, outputs, 1, inputs);
1555 }
1556
1557
VisitTryTruncateFloat64ToUint64(Node * node)1558 void InstructionSelector::VisitTryTruncateFloat64ToUint64(Node* node) {
1559 Arm64OperandGenerator g(this);
1560
1561 InstructionOperand inputs[] = {g.UseRegister(node->InputAt(0))};
1562 InstructionOperand outputs[2];
1563 size_t output_count = 0;
1564 outputs[output_count++] = g.DefineAsRegister(node);
1565
1566 Node* success_output = NodeProperties::FindProjection(node, 1);
1567 if (success_output) {
1568 outputs[output_count++] = g.DefineAsRegister(success_output);
1569 }
1570
1571 Emit(kArm64Float64ToUint64, output_count, outputs, 1, inputs);
1572 }
1573
1574
VisitChangeInt32ToInt64(Node * node)1575 void InstructionSelector::VisitChangeInt32ToInt64(Node* node) {
1576 Node* value = node->InputAt(0);
1577 if (value->opcode() == IrOpcode::kLoad && CanCover(node, value)) {
1578 // Generate sign-extending load.
1579 LoadRepresentation load_rep = LoadRepresentationOf(value->op());
1580 MachineRepresentation rep = load_rep.representation();
1581 InstructionCode opcode = kArchNop;
1582 ImmediateMode immediate_mode = kNoImmediate;
1583 switch (rep) {
1584 case MachineRepresentation::kBit: // Fall through.
1585 case MachineRepresentation::kWord8:
1586 opcode = load_rep.IsSigned() ? kArm64Ldrsb : kArm64Ldrb;
1587 immediate_mode = kLoadStoreImm8;
1588 break;
1589 case MachineRepresentation::kWord16:
1590 opcode = load_rep.IsSigned() ? kArm64Ldrsh : kArm64Ldrh;
1591 immediate_mode = kLoadStoreImm16;
1592 break;
1593 case MachineRepresentation::kWord32:
1594 opcode = kArm64Ldrsw;
1595 immediate_mode = kLoadStoreImm32;
1596 break;
1597 default:
1598 UNREACHABLE();
1599 return;
1600 }
1601 EmitLoad(this, value, opcode, immediate_mode, rep, node);
1602 } else {
1603 VisitRR(this, kArm64Sxtw, node);
1604 }
1605 }
1606
1607
VisitChangeUint32ToUint64(Node * node)1608 void InstructionSelector::VisitChangeUint32ToUint64(Node* node) {
1609 Arm64OperandGenerator g(this);
1610 Node* value = node->InputAt(0);
1611 switch (value->opcode()) {
1612 case IrOpcode::kWord32And:
1613 case IrOpcode::kWord32Or:
1614 case IrOpcode::kWord32Xor:
1615 case IrOpcode::kWord32Shl:
1616 case IrOpcode::kWord32Shr:
1617 case IrOpcode::kWord32Sar:
1618 case IrOpcode::kWord32Ror:
1619 case IrOpcode::kWord32Equal:
1620 case IrOpcode::kInt32Add:
1621 case IrOpcode::kInt32AddWithOverflow:
1622 case IrOpcode::kInt32Sub:
1623 case IrOpcode::kInt32SubWithOverflow:
1624 case IrOpcode::kInt32Mul:
1625 case IrOpcode::kInt32MulHigh:
1626 case IrOpcode::kInt32Div:
1627 case IrOpcode::kInt32Mod:
1628 case IrOpcode::kInt32LessThan:
1629 case IrOpcode::kInt32LessThanOrEqual:
1630 case IrOpcode::kUint32Div:
1631 case IrOpcode::kUint32LessThan:
1632 case IrOpcode::kUint32LessThanOrEqual:
1633 case IrOpcode::kUint32Mod:
1634 case IrOpcode::kUint32MulHigh: {
1635 // 32-bit operations will write their result in a W register (implicitly
1636 // clearing the top 32-bit of the corresponding X register) so the
1637 // zero-extension is a no-op.
1638 Emit(kArchNop, g.DefineSameAsFirst(node), g.Use(value));
1639 return;
1640 }
1641 case IrOpcode::kLoad: {
1642 // As for the operations above, a 32-bit load will implicitly clear the
1643 // top 32 bits of the destination register.
1644 LoadRepresentation load_rep = LoadRepresentationOf(value->op());
1645 switch (load_rep.representation()) {
1646 case MachineRepresentation::kWord8:
1647 case MachineRepresentation::kWord16:
1648 case MachineRepresentation::kWord32:
1649 Emit(kArchNop, g.DefineSameAsFirst(node), g.Use(value));
1650 return;
1651 default:
1652 break;
1653 }
1654 break;
1655 }
1656 default:
1657 break;
1658 }
1659 Emit(kArm64Mov32, g.DefineAsRegister(node), g.UseRegister(value));
1660 }
1661
VisitTruncateInt64ToInt32(Node * node)1662 void InstructionSelector::VisitTruncateInt64ToInt32(Node* node) {
1663 Arm64OperandGenerator g(this);
1664 Node* value = node->InputAt(0);
1665 // The top 32 bits in the 64-bit register will be undefined, and
1666 // must not be used by a dependent node.
1667 Emit(kArchNop, g.DefineSameAsFirst(node), g.UseRegister(value));
1668 }
1669
VisitFloat64Mod(Node * node)1670 void InstructionSelector::VisitFloat64Mod(Node* node) {
1671 Arm64OperandGenerator g(this);
1672 Emit(kArm64Float64Mod, g.DefineAsFixed(node, d0),
1673 g.UseFixed(node->InputAt(0), d0),
1674 g.UseFixed(node->InputAt(1), d1))->MarkAsCall();
1675 }
1676
VisitFloat64Ieee754Binop(Node * node,InstructionCode opcode)1677 void InstructionSelector::VisitFloat64Ieee754Binop(Node* node,
1678 InstructionCode opcode) {
1679 Arm64OperandGenerator g(this);
1680 Emit(opcode, g.DefineAsFixed(node, d0), g.UseFixed(node->InputAt(0), d0),
1681 g.UseFixed(node->InputAt(1), d1))
1682 ->MarkAsCall();
1683 }
1684
VisitFloat64Ieee754Unop(Node * node,InstructionCode opcode)1685 void InstructionSelector::VisitFloat64Ieee754Unop(Node* node,
1686 InstructionCode opcode) {
1687 Arm64OperandGenerator g(this);
1688 Emit(opcode, g.DefineAsFixed(node, d0), g.UseFixed(node->InputAt(0), d0))
1689 ->MarkAsCall();
1690 }
1691
EmitPrepareArguments(ZoneVector<PushParameter> * arguments,const CallDescriptor * call_descriptor,Node * node)1692 void InstructionSelector::EmitPrepareArguments(
1693 ZoneVector<PushParameter>* arguments, const CallDescriptor* call_descriptor,
1694 Node* node) {
1695 Arm64OperandGenerator g(this);
1696
1697 // `arguments` includes alignment "holes". This means that slots bigger than
1698 // kPointerSize, e.g. Simd128, will span across multiple arguments.
1699 int claim_count = static_cast<int>(arguments->size());
1700 int slot = claim_count - 1;
1701 claim_count = RoundUp(claim_count, 2);
1702 // Bump the stack pointer(s).
1703 if (claim_count > 0) {
1704 // TODO(titzer): claim and poke probably take small immediates.
1705 // TODO(titzer): it would be better to bump the sp here only
1706 // and emit paired stores with increment for non c frames.
1707 Emit(kArm64Claim, g.NoOutput(), g.TempImmediate(claim_count));
1708 }
1709
1710 if (claim_count > 0) {
1711 // Store padding, which might be overwritten.
1712 Emit(kArm64Poke, g.NoOutput(), g.UseImmediate(0),
1713 g.TempImmediate(claim_count - 1));
1714 }
1715
1716 // Poke the arguments into the stack.
1717 while (slot >= 0) {
1718 Node* input_node = (*arguments)[slot].node;
1719 // Skip any alignment holes in pushed nodes.
1720 if (input_node != nullptr) {
1721 Emit(kArm64Poke, g.NoOutput(), g.UseRegister(input_node),
1722 g.TempImmediate(slot));
1723 }
1724 slot--;
1725 // TODO(ahaas): Poke arguments in pairs if two subsequent arguments have the
1726 // same type.
1727 // Emit(kArm64PokePair, g.NoOutput(), g.UseRegister((*arguments)[slot]),
1728 // g.UseRegister((*arguments)[slot - 1]), g.TempImmediate(slot));
1729 // slot -= 2;
1730 }
1731 }
1732
EmitPrepareResults(ZoneVector<PushParameter> * results,const CallDescriptor * call_descriptor,Node * node)1733 void InstructionSelector::EmitPrepareResults(
1734 ZoneVector<PushParameter>* results, const CallDescriptor* call_descriptor,
1735 Node* node) {
1736 Arm64OperandGenerator g(this);
1737
1738 int reverse_slot = 0;
1739 for (PushParameter output : *results) {
1740 if (!output.location.IsCallerFrameSlot()) continue;
1741 reverse_slot += output.location.GetSizeInPointers();
1742 // Skip any alignment holes in nodes.
1743 if (output.node == nullptr) continue;
1744 DCHECK(!call_descriptor->IsCFunctionCall());
1745
1746 if (output.location.GetType() == MachineType::Float32()) {
1747 MarkAsFloat32(output.node);
1748 } else if (output.location.GetType() == MachineType::Float64()) {
1749 MarkAsFloat64(output.node);
1750 }
1751
1752 Emit(kArm64Peek, g.DefineAsRegister(output.node),
1753 g.UseImmediate(reverse_slot));
1754 }
1755 }
1756
IsTailCallAddressImmediate()1757 bool InstructionSelector::IsTailCallAddressImmediate() { return false; }
1758
GetTempsCountForTailCallFromJSFunction()1759 int InstructionSelector::GetTempsCountForTailCallFromJSFunction() { return 3; }
1760
1761 namespace {
1762
1763 // Shared routine for multiple compare operations.
VisitCompare(InstructionSelector * selector,InstructionCode opcode,InstructionOperand left,InstructionOperand right,FlagsContinuation * cont)1764 void VisitCompare(InstructionSelector* selector, InstructionCode opcode,
1765 InstructionOperand left, InstructionOperand right,
1766 FlagsContinuation* cont) {
1767 selector->EmitWithContinuation(opcode, left, right, cont);
1768 }
1769
1770
1771 // Shared routine for multiple word compare operations.
VisitWordCompare(InstructionSelector * selector,Node * node,InstructionCode opcode,FlagsContinuation * cont,bool commutative,ImmediateMode immediate_mode)1772 void VisitWordCompare(InstructionSelector* selector, Node* node,
1773 InstructionCode opcode, FlagsContinuation* cont,
1774 bool commutative, ImmediateMode immediate_mode) {
1775 Arm64OperandGenerator g(selector);
1776 Node* left = node->InputAt(0);
1777 Node* right = node->InputAt(1);
1778
1779 if (right->opcode() == IrOpcode::kLoadStackPointer ||
1780 g.CanBeImmediate(left, immediate_mode)) {
1781 if (!commutative) cont->Commute();
1782 std::swap(left, right);
1783 }
1784
1785 // Match immediates on left or right side of comparison.
1786 if (g.CanBeImmediate(right, immediate_mode)) {
1787 VisitCompare(selector, opcode,
1788 g.UseRegisterOrStackPointer(left, opcode == kArm64Cmp),
1789 g.UseImmediate(right), cont);
1790 } else {
1791 VisitCompare(selector, opcode,
1792 g.UseRegisterOrStackPointer(left, opcode == kArm64Cmp),
1793 g.UseRegister(right), cont);
1794 }
1795 }
1796
1797 // This function checks whether we can convert:
1798 // ((a <op> b) cmp 0), b.<cond>
1799 // to:
1800 // (a <ops> b), b.<cond'>
1801 // where <ops> is the flag setting version of <op>.
1802 // We only generate conditions <cond'> that are a combination of the N
1803 // and Z flags. This avoids the need to make this function dependent on
1804 // the flag-setting operation.
CanUseFlagSettingBinop(FlagsCondition cond)1805 bool CanUseFlagSettingBinop(FlagsCondition cond) {
1806 switch (cond) {
1807 case kEqual:
1808 case kNotEqual:
1809 case kSignedLessThan:
1810 case kSignedGreaterThanOrEqual:
1811 case kUnsignedLessThanOrEqual: // x <= 0 -> x == 0
1812 case kUnsignedGreaterThan: // x > 0 -> x != 0
1813 return true;
1814 default:
1815 return false;
1816 }
1817 }
1818
1819 // Map <cond> to <cond'> so that the following transformation is possible:
1820 // ((a <op> b) cmp 0), b.<cond>
1821 // to:
1822 // (a <ops> b), b.<cond'>
1823 // where <ops> is the flag setting version of <op>.
MapForFlagSettingBinop(FlagsCondition cond)1824 FlagsCondition MapForFlagSettingBinop(FlagsCondition cond) {
1825 DCHECK(CanUseFlagSettingBinop(cond));
1826 switch (cond) {
1827 case kEqual:
1828 case kNotEqual:
1829 return cond;
1830 case kSignedLessThan:
1831 return kNegative;
1832 case kSignedGreaterThanOrEqual:
1833 return kPositiveOrZero;
1834 case kUnsignedLessThanOrEqual: // x <= 0 -> x == 0
1835 return kEqual;
1836 case kUnsignedGreaterThan: // x > 0 -> x != 0
1837 return kNotEqual;
1838 default:
1839 UNREACHABLE();
1840 }
1841 }
1842
1843 // This function checks if we can perform the transformation:
1844 // ((a <op> b) cmp 0), b.<cond>
1845 // to:
1846 // (a <ops> b), b.<cond'>
1847 // where <ops> is the flag setting version of <op>, and if so,
1848 // updates {node}, {opcode} and {cont} accordingly.
MaybeReplaceCmpZeroWithFlagSettingBinop(InstructionSelector * selector,Node ** node,Node * binop,ArchOpcode * opcode,FlagsCondition cond,FlagsContinuation * cont,ImmediateMode * immediate_mode)1849 void MaybeReplaceCmpZeroWithFlagSettingBinop(InstructionSelector* selector,
1850 Node** node, Node* binop,
1851 ArchOpcode* opcode,
1852 FlagsCondition cond,
1853 FlagsContinuation* cont,
1854 ImmediateMode* immediate_mode) {
1855 ArchOpcode binop_opcode;
1856 ArchOpcode no_output_opcode;
1857 ImmediateMode binop_immediate_mode;
1858 switch (binop->opcode()) {
1859 case IrOpcode::kInt32Add:
1860 binop_opcode = kArm64Add32;
1861 no_output_opcode = kArm64Cmn32;
1862 binop_immediate_mode = kArithmeticImm;
1863 break;
1864 case IrOpcode::kWord32And:
1865 binop_opcode = kArm64And32;
1866 no_output_opcode = kArm64Tst32;
1867 binop_immediate_mode = kLogical32Imm;
1868 break;
1869 default:
1870 UNREACHABLE();
1871 return;
1872 }
1873 if (selector->CanCover(*node, binop)) {
1874 // The comparison is the only user of the add or and, so we can generate
1875 // a cmn or tst instead.
1876 cont->Overwrite(MapForFlagSettingBinop(cond));
1877 *opcode = no_output_opcode;
1878 *node = binop;
1879 *immediate_mode = binop_immediate_mode;
1880 } else if (selector->IsOnlyUserOfNodeInSameBlock(*node, binop)) {
1881 // We can also handle the case where the add and the compare are in the
1882 // same basic block, and the compare is the only use of add in this basic
1883 // block (the add has users in other basic blocks).
1884 cont->Overwrite(MapForFlagSettingBinop(cond));
1885 *opcode = binop_opcode;
1886 *node = binop;
1887 *immediate_mode = binop_immediate_mode;
1888 }
1889 }
1890
1891 // Map {cond} to kEqual or kNotEqual, so that we can select
1892 // either TBZ or TBNZ when generating code for:
1893 // (x cmp 0), b.{cond}
MapForTbz(FlagsCondition cond)1894 FlagsCondition MapForTbz(FlagsCondition cond) {
1895 switch (cond) {
1896 case kSignedLessThan: // generate TBNZ
1897 return kNotEqual;
1898 case kSignedGreaterThanOrEqual: // generate TBZ
1899 return kEqual;
1900 default:
1901 UNREACHABLE();
1902 }
1903 }
1904
1905 // Map {cond} to kEqual or kNotEqual, so that we can select
1906 // either CBZ or CBNZ when generating code for:
1907 // (x cmp 0), b.{cond}
MapForCbz(FlagsCondition cond)1908 FlagsCondition MapForCbz(FlagsCondition cond) {
1909 switch (cond) {
1910 case kEqual: // generate CBZ
1911 case kNotEqual: // generate CBNZ
1912 return cond;
1913 case kUnsignedLessThanOrEqual: // generate CBZ
1914 return kEqual;
1915 case kUnsignedGreaterThan: // generate CBNZ
1916 return kNotEqual;
1917 default:
1918 UNREACHABLE();
1919 }
1920 }
1921
EmitBranchOrDeoptimize(InstructionSelector * selector,InstructionCode opcode,InstructionOperand value,FlagsContinuation * cont)1922 void EmitBranchOrDeoptimize(InstructionSelector* selector,
1923 InstructionCode opcode, InstructionOperand value,
1924 FlagsContinuation* cont) {
1925 DCHECK(cont->IsBranch() || cont->IsDeoptimize());
1926 selector->EmitWithContinuation(opcode, value, cont);
1927 }
1928
1929 // Try to emit TBZ, TBNZ, CBZ or CBNZ for certain comparisons of {node}
1930 // against {value}, depending on the condition.
TryEmitCbzOrTbz(InstructionSelector * selector,Node * node,uint32_t value,Node * user,FlagsCondition cond,FlagsContinuation * cont)1931 bool TryEmitCbzOrTbz(InstructionSelector* selector, Node* node, uint32_t value,
1932 Node* user, FlagsCondition cond, FlagsContinuation* cont) {
1933 // Branch poisoning requires flags to be set, so when it's enabled for
1934 // a particular branch, we shouldn't be applying the cbz/tbz optimization.
1935 DCHECK(!cont->IsPoisoned());
1936 // Only handle branches and deoptimisations.
1937 if (!cont->IsBranch() && !cont->IsDeoptimize()) return false;
1938
1939 switch (cond) {
1940 case kSignedLessThan:
1941 case kSignedGreaterThanOrEqual: {
1942 // Here we handle sign tests, aka. comparisons with zero.
1943 if (value != 0) return false;
1944 // We don't generate TBZ/TBNZ for deoptimisations, as they have a
1945 // shorter range than conditional branches and generating them for
1946 // deoptimisations results in more veneers.
1947 if (cont->IsDeoptimize()) return false;
1948 Arm64OperandGenerator g(selector);
1949 cont->Overwrite(MapForTbz(cond));
1950 Int32Matcher m(node);
1951 if (m.IsFloat64ExtractHighWord32() && selector->CanCover(user, node)) {
1952 // SignedLessThan(Float64ExtractHighWord32(x), 0) and
1953 // SignedGreaterThanOrEqual(Float64ExtractHighWord32(x), 0) essentially
1954 // check the sign bit of a 64-bit floating point value.
1955 InstructionOperand temp = g.TempRegister();
1956 selector->Emit(kArm64U64MoveFloat64, temp,
1957 g.UseRegister(node->InputAt(0)));
1958 selector->EmitWithContinuation(kArm64TestAndBranch, temp,
1959 g.TempImmediate(63), cont);
1960 return true;
1961 }
1962 selector->EmitWithContinuation(kArm64TestAndBranch32, g.UseRegister(node),
1963 g.TempImmediate(31), cont);
1964 return true;
1965 }
1966 case kEqual:
1967 case kNotEqual: {
1968 if (node->opcode() == IrOpcode::kWord32And) {
1969 // Emit a tbz/tbnz if we are comparing with a single-bit mask:
1970 // Branch(Word32Equal(Word32And(x, 1 << N), 1 << N), true, false)
1971 Int32BinopMatcher m_and(node);
1972 if (cont->IsBranch() && base::bits::IsPowerOfTwo(value) &&
1973 m_and.right().Is(value) && selector->CanCover(user, node)) {
1974 Arm64OperandGenerator g(selector);
1975 // In the code generator, Equal refers to a bit being cleared. We want
1976 // the opposite here so negate the condition.
1977 cont->Negate();
1978 selector->EmitWithContinuation(
1979 kArm64TestAndBranch32, g.UseRegister(m_and.left().node()),
1980 g.TempImmediate(base::bits::CountTrailingZeros(value)), cont);
1981 return true;
1982 }
1983 }
1984 V8_FALLTHROUGH;
1985 }
1986 case kUnsignedLessThanOrEqual:
1987 case kUnsignedGreaterThan: {
1988 if (value != 0) return false;
1989 Arm64OperandGenerator g(selector);
1990 cont->Overwrite(MapForCbz(cond));
1991 EmitBranchOrDeoptimize(selector, kArm64CompareAndBranch32,
1992 g.UseRegister(node), cont);
1993 return true;
1994 }
1995 default:
1996 return false;
1997 }
1998 }
1999
VisitWord32Compare(InstructionSelector * selector,Node * node,FlagsContinuation * cont)2000 void VisitWord32Compare(InstructionSelector* selector, Node* node,
2001 FlagsContinuation* cont) {
2002 Int32BinopMatcher m(node);
2003 FlagsCondition cond = cont->condition();
2004 if (!cont->IsPoisoned()) {
2005 if (m.right().HasValue()) {
2006 if (TryEmitCbzOrTbz(selector, m.left().node(), m.right().Value(), node,
2007 cond, cont)) {
2008 return;
2009 }
2010 } else if (m.left().HasValue()) {
2011 FlagsCondition commuted_cond = CommuteFlagsCondition(cond);
2012 if (TryEmitCbzOrTbz(selector, m.right().node(), m.left().Value(), node,
2013 commuted_cond, cont)) {
2014 return;
2015 }
2016 }
2017 }
2018 ArchOpcode opcode = kArm64Cmp32;
2019 ImmediateMode immediate_mode = kArithmeticImm;
2020 if (m.right().Is(0) && (m.left().IsInt32Add() || m.left().IsWord32And())) {
2021 // Emit flag setting add/and instructions for comparisons against zero.
2022 if (CanUseFlagSettingBinop(cond)) {
2023 Node* binop = m.left().node();
2024 MaybeReplaceCmpZeroWithFlagSettingBinop(selector, &node, binop, &opcode,
2025 cond, cont, &immediate_mode);
2026 }
2027 } else if (m.left().Is(0) &&
2028 (m.right().IsInt32Add() || m.right().IsWord32And())) {
2029 // Same as above, but we need to commute the condition before we
2030 // continue with the rest of the checks.
2031 FlagsCondition commuted_cond = CommuteFlagsCondition(cond);
2032 if (CanUseFlagSettingBinop(commuted_cond)) {
2033 Node* binop = m.right().node();
2034 MaybeReplaceCmpZeroWithFlagSettingBinop(selector, &node, binop, &opcode,
2035 commuted_cond, cont,
2036 &immediate_mode);
2037 }
2038 } else if (m.right().IsInt32Sub() && (cond == kEqual || cond == kNotEqual)) {
2039 // Select negated compare for comparisons with negated right input.
2040 // Only do this for kEqual and kNotEqual, which do not depend on the
2041 // C and V flags, as those flags will be different with CMN when the
2042 // right-hand side of the original subtraction is INT_MIN.
2043 Node* sub = m.right().node();
2044 Int32BinopMatcher msub(sub);
2045 if (msub.left().Is(0)) {
2046 bool can_cover = selector->CanCover(node, sub);
2047 node->ReplaceInput(1, msub.right().node());
2048 // Even if the comparison node covers the subtraction, after the input
2049 // replacement above, the node still won't cover the input to the
2050 // subtraction; the subtraction still uses it.
2051 // In order to get shifted operations to work, we must remove the rhs
2052 // input to the subtraction, as TryMatchAnyShift requires this node to
2053 // cover the input shift. We do this by setting it to the lhs input,
2054 // as we know it's zero, and the result of the subtraction isn't used by
2055 // any other node.
2056 if (can_cover) sub->ReplaceInput(1, msub.left().node());
2057 opcode = kArm64Cmn32;
2058 }
2059 }
2060 VisitBinop<Int32BinopMatcher>(selector, node, opcode, immediate_mode, cont);
2061 }
2062
2063
VisitWordTest(InstructionSelector * selector,Node * node,InstructionCode opcode,FlagsContinuation * cont)2064 void VisitWordTest(InstructionSelector* selector, Node* node,
2065 InstructionCode opcode, FlagsContinuation* cont) {
2066 Arm64OperandGenerator g(selector);
2067 VisitCompare(selector, opcode, g.UseRegister(node), g.UseRegister(node),
2068 cont);
2069 }
2070
2071
VisitWord32Test(InstructionSelector * selector,Node * node,FlagsContinuation * cont)2072 void VisitWord32Test(InstructionSelector* selector, Node* node,
2073 FlagsContinuation* cont) {
2074 VisitWordTest(selector, node, kArm64Tst32, cont);
2075 }
2076
2077
VisitWord64Test(InstructionSelector * selector,Node * node,FlagsContinuation * cont)2078 void VisitWord64Test(InstructionSelector* selector, Node* node,
2079 FlagsContinuation* cont) {
2080 VisitWordTest(selector, node, kArm64Tst, cont);
2081 }
2082
2083 template <typename Matcher>
2084 struct TestAndBranchMatcher {
TestAndBranchMatcherv8::internal::compiler::__anon47426b030411::TestAndBranchMatcher2085 TestAndBranchMatcher(Node* node, FlagsContinuation* cont)
2086 : matches_(false), cont_(cont), matcher_(node) {
2087 Initialize();
2088 }
Matchesv8::internal::compiler::__anon47426b030411::TestAndBranchMatcher2089 bool Matches() const { return matches_; }
2090
bitv8::internal::compiler::__anon47426b030411::TestAndBranchMatcher2091 unsigned bit() const {
2092 DCHECK(Matches());
2093 return base::bits::CountTrailingZeros(matcher_.right().Value());
2094 }
2095
inputv8::internal::compiler::__anon47426b030411::TestAndBranchMatcher2096 Node* input() const {
2097 DCHECK(Matches());
2098 return matcher_.left().node();
2099 }
2100
2101 private:
2102 bool matches_;
2103 FlagsContinuation* cont_;
2104 Matcher matcher_;
2105
Initializev8::internal::compiler::__anon47426b030411::TestAndBranchMatcher2106 void Initialize() {
2107 if (cont_->IsBranch() && !cont_->IsPoisoned() &&
2108 matcher_.right().HasValue() &&
2109 base::bits::IsPowerOfTwo(matcher_.right().Value())) {
2110 // If the mask has only one bit set, we can use tbz/tbnz.
2111 DCHECK((cont_->condition() == kEqual) ||
2112 (cont_->condition() == kNotEqual));
2113 matches_ = true;
2114 } else {
2115 matches_ = false;
2116 }
2117 }
2118 };
2119
2120 // Shared routine for multiple float32 compare operations.
VisitFloat32Compare(InstructionSelector * selector,Node * node,FlagsContinuation * cont)2121 void VisitFloat32Compare(InstructionSelector* selector, Node* node,
2122 FlagsContinuation* cont) {
2123 Arm64OperandGenerator g(selector);
2124 Float32BinopMatcher m(node);
2125 if (m.right().Is(0.0f)) {
2126 VisitCompare(selector, kArm64Float32Cmp, g.UseRegister(m.left().node()),
2127 g.UseImmediate(m.right().node()), cont);
2128 } else if (m.left().Is(0.0f)) {
2129 cont->Commute();
2130 VisitCompare(selector, kArm64Float32Cmp, g.UseRegister(m.right().node()),
2131 g.UseImmediate(m.left().node()), cont);
2132 } else {
2133 VisitCompare(selector, kArm64Float32Cmp, g.UseRegister(m.left().node()),
2134 g.UseRegister(m.right().node()), cont);
2135 }
2136 }
2137
2138
2139 // Shared routine for multiple float64 compare operations.
VisitFloat64Compare(InstructionSelector * selector,Node * node,FlagsContinuation * cont)2140 void VisitFloat64Compare(InstructionSelector* selector, Node* node,
2141 FlagsContinuation* cont) {
2142 Arm64OperandGenerator g(selector);
2143 Float64BinopMatcher m(node);
2144 if (m.right().Is(0.0)) {
2145 VisitCompare(selector, kArm64Float64Cmp, g.UseRegister(m.left().node()),
2146 g.UseImmediate(m.right().node()), cont);
2147 } else if (m.left().Is(0.0)) {
2148 cont->Commute();
2149 VisitCompare(selector, kArm64Float64Cmp, g.UseRegister(m.right().node()),
2150 g.UseImmediate(m.left().node()), cont);
2151 } else {
2152 VisitCompare(selector, kArm64Float64Cmp, g.UseRegister(m.left().node()),
2153 g.UseRegister(m.right().node()), cont);
2154 }
2155 }
2156
VisitAtomicExchange(InstructionSelector * selector,Node * node,ArchOpcode opcode)2157 void VisitAtomicExchange(InstructionSelector* selector, Node* node,
2158 ArchOpcode opcode) {
2159 Arm64OperandGenerator g(selector);
2160 Node* base = node->InputAt(0);
2161 Node* index = node->InputAt(1);
2162 Node* value = node->InputAt(2);
2163 InstructionOperand inputs[] = {g.UseRegister(base), g.UseRegister(index),
2164 g.UseUniqueRegister(value)};
2165 InstructionOperand outputs[] = {g.DefineAsRegister(node)};
2166 InstructionOperand temps[] = {g.TempRegister(), g.TempRegister()};
2167 InstructionCode code = opcode | AddressingModeField::encode(kMode_MRR);
2168 selector->Emit(code, arraysize(outputs), outputs, arraysize(inputs), inputs,
2169 arraysize(temps), temps);
2170 }
2171
VisitAtomicCompareExchange(InstructionSelector * selector,Node * node,ArchOpcode opcode)2172 void VisitAtomicCompareExchange(InstructionSelector* selector, Node* node,
2173 ArchOpcode opcode) {
2174 Arm64OperandGenerator g(selector);
2175 Node* base = node->InputAt(0);
2176 Node* index = node->InputAt(1);
2177 Node* old_value = node->InputAt(2);
2178 Node* new_value = node->InputAt(3);
2179 InstructionOperand inputs[] = {g.UseRegister(base), g.UseRegister(index),
2180 g.UseUniqueRegister(old_value),
2181 g.UseUniqueRegister(new_value)};
2182 InstructionOperand outputs[] = {g.DefineAsRegister(node)};
2183 InstructionOperand temps[] = {g.TempRegister(), g.TempRegister()};
2184 InstructionCode code = opcode | AddressingModeField::encode(kMode_MRR);
2185 selector->Emit(code, arraysize(outputs), outputs, arraysize(inputs), inputs,
2186 arraysize(temps), temps);
2187 }
2188
VisitAtomicLoad(InstructionSelector * selector,Node * node,ArchOpcode opcode)2189 void VisitAtomicLoad(InstructionSelector* selector, Node* node,
2190 ArchOpcode opcode) {
2191 Arm64OperandGenerator g(selector);
2192 Node* base = node->InputAt(0);
2193 Node* index = node->InputAt(1);
2194 InstructionOperand inputs[] = {g.UseRegister(base), g.UseRegister(index)};
2195 InstructionOperand outputs[] = {g.DefineAsRegister(node)};
2196 InstructionOperand temps[] = {g.TempRegister()};
2197 InstructionCode code = opcode | AddressingModeField::encode(kMode_MRR);
2198 selector->Emit(code, arraysize(outputs), outputs, arraysize(inputs), inputs,
2199 arraysize(temps), temps);
2200 }
2201
VisitAtomicStore(InstructionSelector * selector,Node * node,ArchOpcode opcode)2202 void VisitAtomicStore(InstructionSelector* selector, Node* node,
2203 ArchOpcode opcode) {
2204 Arm64OperandGenerator g(selector);
2205 Node* base = node->InputAt(0);
2206 Node* index = node->InputAt(1);
2207 Node* value = node->InputAt(2);
2208 InstructionOperand inputs[] = {g.UseRegister(base), g.UseRegister(index),
2209 g.UseUniqueRegister(value)};
2210 InstructionOperand temps[] = {g.TempRegister()};
2211 InstructionCode code = opcode | AddressingModeField::encode(kMode_MRR);
2212 selector->Emit(code, 0, nullptr, arraysize(inputs), inputs, arraysize(temps),
2213 temps);
2214 }
2215
VisitAtomicBinop(InstructionSelector * selector,Node * node,ArchOpcode opcode)2216 void VisitAtomicBinop(InstructionSelector* selector, Node* node,
2217 ArchOpcode opcode) {
2218 Arm64OperandGenerator g(selector);
2219 Node* base = node->InputAt(0);
2220 Node* index = node->InputAt(1);
2221 Node* value = node->InputAt(2);
2222 AddressingMode addressing_mode = kMode_MRR;
2223 InstructionOperand inputs[] = {g.UseRegister(base), g.UseRegister(index),
2224 g.UseUniqueRegister(value)};
2225 InstructionOperand outputs[] = {g.DefineAsRegister(node)};
2226 InstructionOperand temps[] = {g.TempRegister(), g.TempRegister(),
2227 g.TempRegister()};
2228 InstructionCode code = opcode | AddressingModeField::encode(addressing_mode);
2229 selector->Emit(code, arraysize(outputs), outputs, arraysize(inputs), inputs,
2230 arraysize(temps), temps);
2231 }
2232
2233 } // namespace
2234
VisitWordCompareZero(Node * user,Node * value,FlagsContinuation * cont)2235 void InstructionSelector::VisitWordCompareZero(Node* user, Node* value,
2236 FlagsContinuation* cont) {
2237 Arm64OperandGenerator g(this);
2238 // Try to combine with comparisons against 0 by simply inverting the branch.
2239 while (value->opcode() == IrOpcode::kWord32Equal && CanCover(user, value)) {
2240 Int32BinopMatcher m(value);
2241 if (!m.right().Is(0)) break;
2242
2243 user = value;
2244 value = m.left().node();
2245 cont->Negate();
2246 }
2247
2248 // Try to match bit checks to create TBZ/TBNZ instructions.
2249 // Unlike the switch below, CanCover check is not needed here.
2250 // If there are several uses of the given operation, we will generate a TBZ
2251 // instruction for each. This is useful even if there are other uses of the
2252 // arithmetic result, because it moves dependencies further back.
2253 switch (value->opcode()) {
2254 case IrOpcode::kWord64Equal: {
2255 Int64BinopMatcher m(value);
2256 if (m.right().Is(0)) {
2257 Node* const left = m.left().node();
2258 if (left->opcode() == IrOpcode::kWord64And) {
2259 // Attempt to merge the Word64Equal(Word64And(x, y), 0) comparison
2260 // into a tbz/tbnz instruction.
2261 TestAndBranchMatcher<Uint64BinopMatcher> tbm(left, cont);
2262 if (tbm.Matches()) {
2263 Arm64OperandGenerator gen(this);
2264 cont->OverwriteAndNegateIfEqual(kEqual);
2265 this->EmitWithContinuation(kArm64TestAndBranch,
2266 gen.UseRegister(tbm.input()),
2267 gen.TempImmediate(tbm.bit()), cont);
2268 return;
2269 }
2270 }
2271 }
2272 break;
2273 }
2274 case IrOpcode::kWord32And: {
2275 TestAndBranchMatcher<Uint32BinopMatcher> tbm(value, cont);
2276 if (tbm.Matches()) {
2277 Arm64OperandGenerator gen(this);
2278 this->EmitWithContinuation(kArm64TestAndBranch32,
2279 gen.UseRegister(tbm.input()),
2280 gen.TempImmediate(tbm.bit()), cont);
2281 return;
2282 }
2283 break;
2284 }
2285 case IrOpcode::kWord64And: {
2286 TestAndBranchMatcher<Uint64BinopMatcher> tbm(value, cont);
2287 if (tbm.Matches()) {
2288 Arm64OperandGenerator gen(this);
2289 this->EmitWithContinuation(kArm64TestAndBranch,
2290 gen.UseRegister(tbm.input()),
2291 gen.TempImmediate(tbm.bit()), cont);
2292 return;
2293 }
2294 break;
2295 }
2296 default:
2297 break;
2298 }
2299
2300 if (CanCover(user, value)) {
2301 switch (value->opcode()) {
2302 case IrOpcode::kWord32Equal:
2303 cont->OverwriteAndNegateIfEqual(kEqual);
2304 return VisitWord32Compare(this, value, cont);
2305 case IrOpcode::kInt32LessThan:
2306 cont->OverwriteAndNegateIfEqual(kSignedLessThan);
2307 return VisitWord32Compare(this, value, cont);
2308 case IrOpcode::kInt32LessThanOrEqual:
2309 cont->OverwriteAndNegateIfEqual(kSignedLessThanOrEqual);
2310 return VisitWord32Compare(this, value, cont);
2311 case IrOpcode::kUint32LessThan:
2312 cont->OverwriteAndNegateIfEqual(kUnsignedLessThan);
2313 return VisitWord32Compare(this, value, cont);
2314 case IrOpcode::kUint32LessThanOrEqual:
2315 cont->OverwriteAndNegateIfEqual(kUnsignedLessThanOrEqual);
2316 return VisitWord32Compare(this, value, cont);
2317 case IrOpcode::kWord64Equal: {
2318 cont->OverwriteAndNegateIfEqual(kEqual);
2319 Int64BinopMatcher m(value);
2320 if (m.right().Is(0)) {
2321 Node* const left = m.left().node();
2322 if (CanCover(value, left) && left->opcode() == IrOpcode::kWord64And) {
2323 return VisitWordCompare(this, left, kArm64Tst, cont, true,
2324 kLogical64Imm);
2325 }
2326 // Merge the Word64Equal(x, 0) comparison into a cbz instruction.
2327 if ((cont->IsBranch() || cont->IsDeoptimize()) &&
2328 !cont->IsPoisoned()) {
2329 EmitBranchOrDeoptimize(this, kArm64CompareAndBranch,
2330 g.UseRegister(left), cont);
2331 return;
2332 }
2333 }
2334 return VisitWordCompare(this, value, kArm64Cmp, cont, false,
2335 kArithmeticImm);
2336 }
2337 case IrOpcode::kInt64LessThan:
2338 cont->OverwriteAndNegateIfEqual(kSignedLessThan);
2339 return VisitWordCompare(this, value, kArm64Cmp, cont, false,
2340 kArithmeticImm);
2341 case IrOpcode::kInt64LessThanOrEqual:
2342 cont->OverwriteAndNegateIfEqual(kSignedLessThanOrEqual);
2343 return VisitWordCompare(this, value, kArm64Cmp, cont, false,
2344 kArithmeticImm);
2345 case IrOpcode::kUint64LessThan:
2346 cont->OverwriteAndNegateIfEqual(kUnsignedLessThan);
2347 return VisitWordCompare(this, value, kArm64Cmp, cont, false,
2348 kArithmeticImm);
2349 case IrOpcode::kUint64LessThanOrEqual:
2350 cont->OverwriteAndNegateIfEqual(kUnsignedLessThanOrEqual);
2351 return VisitWordCompare(this, value, kArm64Cmp, cont, false,
2352 kArithmeticImm);
2353 case IrOpcode::kFloat32Equal:
2354 cont->OverwriteAndNegateIfEqual(kEqual);
2355 return VisitFloat32Compare(this, value, cont);
2356 case IrOpcode::kFloat32LessThan:
2357 cont->OverwriteAndNegateIfEqual(kFloatLessThan);
2358 return VisitFloat32Compare(this, value, cont);
2359 case IrOpcode::kFloat32LessThanOrEqual:
2360 cont->OverwriteAndNegateIfEqual(kFloatLessThanOrEqual);
2361 return VisitFloat32Compare(this, value, cont);
2362 case IrOpcode::kFloat64Equal:
2363 cont->OverwriteAndNegateIfEqual(kEqual);
2364 return VisitFloat64Compare(this, value, cont);
2365 case IrOpcode::kFloat64LessThan:
2366 cont->OverwriteAndNegateIfEqual(kFloatLessThan);
2367 return VisitFloat64Compare(this, value, cont);
2368 case IrOpcode::kFloat64LessThanOrEqual:
2369 cont->OverwriteAndNegateIfEqual(kFloatLessThanOrEqual);
2370 return VisitFloat64Compare(this, value, cont);
2371 case IrOpcode::kProjection:
2372 // Check if this is the overflow output projection of an
2373 // <Operation>WithOverflow node.
2374 if (ProjectionIndexOf(value->op()) == 1u) {
2375 // We cannot combine the <Operation>WithOverflow with this branch
2376 // unless the 0th projection (the use of the actual value of the
2377 // <Operation> is either nullptr, which means there's no use of the
2378 // actual value, or was already defined, which means it is scheduled
2379 // *AFTER* this branch).
2380 Node* const node = value->InputAt(0);
2381 Node* const result = NodeProperties::FindProjection(node, 0);
2382 if (result == nullptr || IsDefined(result)) {
2383 switch (node->opcode()) {
2384 case IrOpcode::kInt32AddWithOverflow:
2385 cont->OverwriteAndNegateIfEqual(kOverflow);
2386 return VisitBinop<Int32BinopMatcher>(this, node, kArm64Add32,
2387 kArithmeticImm, cont);
2388 case IrOpcode::kInt32SubWithOverflow:
2389 cont->OverwriteAndNegateIfEqual(kOverflow);
2390 return VisitBinop<Int32BinopMatcher>(this, node, kArm64Sub32,
2391 kArithmeticImm, cont);
2392 case IrOpcode::kInt32MulWithOverflow:
2393 // ARM64 doesn't set the overflow flag for multiplication, so we
2394 // need to test on kNotEqual. Here is the code sequence used:
2395 // smull result, left, right
2396 // cmp result.X(), Operand(result, SXTW)
2397 cont->OverwriteAndNegateIfEqual(kNotEqual);
2398 return EmitInt32MulWithOverflow(this, node, cont);
2399 case IrOpcode::kInt64AddWithOverflow:
2400 cont->OverwriteAndNegateIfEqual(kOverflow);
2401 return VisitBinop<Int64BinopMatcher>(this, node, kArm64Add,
2402 kArithmeticImm, cont);
2403 case IrOpcode::kInt64SubWithOverflow:
2404 cont->OverwriteAndNegateIfEqual(kOverflow);
2405 return VisitBinop<Int64BinopMatcher>(this, node, kArm64Sub,
2406 kArithmeticImm, cont);
2407 default:
2408 break;
2409 }
2410 }
2411 }
2412 break;
2413 case IrOpcode::kInt32Add:
2414 return VisitWordCompare(this, value, kArm64Cmn32, cont, true,
2415 kArithmeticImm);
2416 case IrOpcode::kInt32Sub:
2417 return VisitWord32Compare(this, value, cont);
2418 case IrOpcode::kWord32And:
2419 return VisitWordCompare(this, value, kArm64Tst32, cont, true,
2420 kLogical32Imm);
2421 case IrOpcode::kWord64And:
2422 return VisitWordCompare(this, value, kArm64Tst, cont, true,
2423 kLogical64Imm);
2424 default:
2425 break;
2426 }
2427 }
2428
2429 // Branch could not be combined with a compare, compare against 0 and branch.
2430 if (!cont->IsPoisoned() && cont->IsBranch()) {
2431 Emit(cont->Encode(kArm64CompareAndBranch32), g.NoOutput(),
2432 g.UseRegister(value), g.Label(cont->true_block()),
2433 g.Label(cont->false_block()));
2434 } else {
2435 EmitWithContinuation(cont->Encode(kArm64Tst32), g.UseRegister(value),
2436 g.UseRegister(value), cont);
2437 }
2438 }
2439
VisitSwitch(Node * node,const SwitchInfo & sw)2440 void InstructionSelector::VisitSwitch(Node* node, const SwitchInfo& sw) {
2441 Arm64OperandGenerator g(this);
2442 InstructionOperand value_operand = g.UseRegister(node->InputAt(0));
2443
2444 // Emit either ArchTableSwitch or ArchLookupSwitch.
2445 if (enable_switch_jump_table_ == kEnableSwitchJumpTable) {
2446 static const size_t kMaxTableSwitchValueRange = 2 << 16;
2447 size_t table_space_cost = 4 + sw.value_range();
2448 size_t table_time_cost = 3;
2449 size_t lookup_space_cost = 3 + 2 * sw.case_count();
2450 size_t lookup_time_cost = sw.case_count();
2451 if (sw.case_count() > 0 &&
2452 table_space_cost + 3 * table_time_cost <=
2453 lookup_space_cost + 3 * lookup_time_cost &&
2454 sw.min_value() > std::numeric_limits<int32_t>::min() &&
2455 sw.value_range() <= kMaxTableSwitchValueRange) {
2456 InstructionOperand index_operand = value_operand;
2457 if (sw.min_value()) {
2458 index_operand = g.TempRegister();
2459 Emit(kArm64Sub32, index_operand, value_operand,
2460 g.TempImmediate(sw.min_value()));
2461 }
2462 // Generate a table lookup.
2463 return EmitTableSwitch(sw, index_operand);
2464 }
2465 }
2466
2467 // Generate a tree of conditional jumps.
2468 return EmitBinarySearchSwitch(sw, value_operand);
2469 }
2470
VisitWord32Equal(Node * const node)2471 void InstructionSelector::VisitWord32Equal(Node* const node) {
2472 Node* const user = node;
2473 FlagsContinuation cont = FlagsContinuation::ForSet(kEqual, node);
2474 Int32BinopMatcher m(user);
2475 if (m.right().Is(0)) {
2476 Node* const value = m.left().node();
2477 if (CanCover(user, value)) {
2478 switch (value->opcode()) {
2479 case IrOpcode::kInt32Add:
2480 case IrOpcode::kWord32And:
2481 return VisitWord32Compare(this, node, &cont);
2482 case IrOpcode::kInt32Sub:
2483 return VisitWordCompare(this, value, kArm64Cmp32, &cont, false,
2484 kArithmeticImm);
2485 case IrOpcode::kWord32Equal: {
2486 // Word32Equal(Word32Equal(x, y), 0) => Word32Compare(x, y, ne).
2487 Int32BinopMatcher mequal(value);
2488 node->ReplaceInput(0, mequal.left().node());
2489 node->ReplaceInput(1, mequal.right().node());
2490 cont.Negate();
2491 // {node} still does not cover its new operands, because {mequal} is
2492 // still using them.
2493 // Since we won't generate any more code for {mequal}, set its
2494 // operands to zero to make sure {node} can cover them.
2495 // This improves pattern matching in VisitWord32Compare.
2496 mequal.node()->ReplaceInput(0, m.right().node());
2497 mequal.node()->ReplaceInput(1, m.right().node());
2498 return VisitWord32Compare(this, node, &cont);
2499 }
2500 default:
2501 break;
2502 }
2503 return VisitWord32Test(this, value, &cont);
2504 }
2505 }
2506 VisitWord32Compare(this, node, &cont);
2507 }
2508
2509
VisitInt32LessThan(Node * node)2510 void InstructionSelector::VisitInt32LessThan(Node* node) {
2511 FlagsContinuation cont = FlagsContinuation::ForSet(kSignedLessThan, node);
2512 VisitWord32Compare(this, node, &cont);
2513 }
2514
2515
VisitInt32LessThanOrEqual(Node * node)2516 void InstructionSelector::VisitInt32LessThanOrEqual(Node* node) {
2517 FlagsContinuation cont =
2518 FlagsContinuation::ForSet(kSignedLessThanOrEqual, node);
2519 VisitWord32Compare(this, node, &cont);
2520 }
2521
2522
VisitUint32LessThan(Node * node)2523 void InstructionSelector::VisitUint32LessThan(Node* node) {
2524 FlagsContinuation cont = FlagsContinuation::ForSet(kUnsignedLessThan, node);
2525 VisitWord32Compare(this, node, &cont);
2526 }
2527
2528
VisitUint32LessThanOrEqual(Node * node)2529 void InstructionSelector::VisitUint32LessThanOrEqual(Node* node) {
2530 FlagsContinuation cont =
2531 FlagsContinuation::ForSet(kUnsignedLessThanOrEqual, node);
2532 VisitWord32Compare(this, node, &cont);
2533 }
2534
2535
VisitWord64Equal(Node * const node)2536 void InstructionSelector::VisitWord64Equal(Node* const node) {
2537 Node* const user = node;
2538 FlagsContinuation cont = FlagsContinuation::ForSet(kEqual, node);
2539 Int64BinopMatcher m(user);
2540 if (m.right().Is(0)) {
2541 Node* const value = m.left().node();
2542 if (CanCover(user, value)) {
2543 switch (value->opcode()) {
2544 case IrOpcode::kWord64And:
2545 return VisitWordCompare(this, value, kArm64Tst, &cont, true,
2546 kLogical64Imm);
2547 default:
2548 break;
2549 }
2550 return VisitWord64Test(this, value, &cont);
2551 }
2552 }
2553 VisitWordCompare(this, node, kArm64Cmp, &cont, false, kArithmeticImm);
2554 }
2555
2556
VisitInt32AddWithOverflow(Node * node)2557 void InstructionSelector::VisitInt32AddWithOverflow(Node* node) {
2558 if (Node* ovf = NodeProperties::FindProjection(node, 1)) {
2559 FlagsContinuation cont = FlagsContinuation::ForSet(kOverflow, ovf);
2560 return VisitBinop<Int32BinopMatcher>(this, node, kArm64Add32,
2561 kArithmeticImm, &cont);
2562 }
2563 FlagsContinuation cont;
2564 VisitBinop<Int32BinopMatcher>(this, node, kArm64Add32, kArithmeticImm, &cont);
2565 }
2566
2567
VisitInt32SubWithOverflow(Node * node)2568 void InstructionSelector::VisitInt32SubWithOverflow(Node* node) {
2569 if (Node* ovf = NodeProperties::FindProjection(node, 1)) {
2570 FlagsContinuation cont = FlagsContinuation::ForSet(kOverflow, ovf);
2571 return VisitBinop<Int32BinopMatcher>(this, node, kArm64Sub32,
2572 kArithmeticImm, &cont);
2573 }
2574 FlagsContinuation cont;
2575 VisitBinop<Int32BinopMatcher>(this, node, kArm64Sub32, kArithmeticImm, &cont);
2576 }
2577
VisitInt32MulWithOverflow(Node * node)2578 void InstructionSelector::VisitInt32MulWithOverflow(Node* node) {
2579 if (Node* ovf = NodeProperties::FindProjection(node, 1)) {
2580 // ARM64 doesn't set the overflow flag for multiplication, so we need to
2581 // test on kNotEqual. Here is the code sequence used:
2582 // smull result, left, right
2583 // cmp result.X(), Operand(result, SXTW)
2584 FlagsContinuation cont = FlagsContinuation::ForSet(kNotEqual, ovf);
2585 return EmitInt32MulWithOverflow(this, node, &cont);
2586 }
2587 FlagsContinuation cont;
2588 EmitInt32MulWithOverflow(this, node, &cont);
2589 }
2590
VisitInt64AddWithOverflow(Node * node)2591 void InstructionSelector::VisitInt64AddWithOverflow(Node* node) {
2592 if (Node* ovf = NodeProperties::FindProjection(node, 1)) {
2593 FlagsContinuation cont = FlagsContinuation::ForSet(kOverflow, ovf);
2594 return VisitBinop<Int64BinopMatcher>(this, node, kArm64Add, kArithmeticImm,
2595 &cont);
2596 }
2597 FlagsContinuation cont;
2598 VisitBinop<Int64BinopMatcher>(this, node, kArm64Add, kArithmeticImm, &cont);
2599 }
2600
2601
VisitInt64SubWithOverflow(Node * node)2602 void InstructionSelector::VisitInt64SubWithOverflow(Node* node) {
2603 if (Node* ovf = NodeProperties::FindProjection(node, 1)) {
2604 FlagsContinuation cont = FlagsContinuation::ForSet(kOverflow, ovf);
2605 return VisitBinop<Int64BinopMatcher>(this, node, kArm64Sub, kArithmeticImm,
2606 &cont);
2607 }
2608 FlagsContinuation cont;
2609 VisitBinop<Int64BinopMatcher>(this, node, kArm64Sub, kArithmeticImm, &cont);
2610 }
2611
2612
VisitInt64LessThan(Node * node)2613 void InstructionSelector::VisitInt64LessThan(Node* node) {
2614 FlagsContinuation cont = FlagsContinuation::ForSet(kSignedLessThan, node);
2615 VisitWordCompare(this, node, kArm64Cmp, &cont, false, kArithmeticImm);
2616 }
2617
2618
VisitInt64LessThanOrEqual(Node * node)2619 void InstructionSelector::VisitInt64LessThanOrEqual(Node* node) {
2620 FlagsContinuation cont =
2621 FlagsContinuation::ForSet(kSignedLessThanOrEqual, node);
2622 VisitWordCompare(this, node, kArm64Cmp, &cont, false, kArithmeticImm);
2623 }
2624
2625
VisitUint64LessThan(Node * node)2626 void InstructionSelector::VisitUint64LessThan(Node* node) {
2627 FlagsContinuation cont = FlagsContinuation::ForSet(kUnsignedLessThan, node);
2628 VisitWordCompare(this, node, kArm64Cmp, &cont, false, kArithmeticImm);
2629 }
2630
2631
VisitUint64LessThanOrEqual(Node * node)2632 void InstructionSelector::VisitUint64LessThanOrEqual(Node* node) {
2633 FlagsContinuation cont =
2634 FlagsContinuation::ForSet(kUnsignedLessThanOrEqual, node);
2635 VisitWordCompare(this, node, kArm64Cmp, &cont, false, kArithmeticImm);
2636 }
2637
2638
VisitFloat32Equal(Node * node)2639 void InstructionSelector::VisitFloat32Equal(Node* node) {
2640 FlagsContinuation cont = FlagsContinuation::ForSet(kEqual, node);
2641 VisitFloat32Compare(this, node, &cont);
2642 }
2643
2644
VisitFloat32LessThan(Node * node)2645 void InstructionSelector::VisitFloat32LessThan(Node* node) {
2646 FlagsContinuation cont = FlagsContinuation::ForSet(kFloatLessThan, node);
2647 VisitFloat32Compare(this, node, &cont);
2648 }
2649
2650
VisitFloat32LessThanOrEqual(Node * node)2651 void InstructionSelector::VisitFloat32LessThanOrEqual(Node* node) {
2652 FlagsContinuation cont =
2653 FlagsContinuation::ForSet(kFloatLessThanOrEqual, node);
2654 VisitFloat32Compare(this, node, &cont);
2655 }
2656
2657
VisitFloat64Equal(Node * node)2658 void InstructionSelector::VisitFloat64Equal(Node* node) {
2659 FlagsContinuation cont = FlagsContinuation::ForSet(kEqual, node);
2660 VisitFloat64Compare(this, node, &cont);
2661 }
2662
2663
VisitFloat64LessThan(Node * node)2664 void InstructionSelector::VisitFloat64LessThan(Node* node) {
2665 FlagsContinuation cont = FlagsContinuation::ForSet(kFloatLessThan, node);
2666 VisitFloat64Compare(this, node, &cont);
2667 }
2668
2669
VisitFloat64LessThanOrEqual(Node * node)2670 void InstructionSelector::VisitFloat64LessThanOrEqual(Node* node) {
2671 FlagsContinuation cont =
2672 FlagsContinuation::ForSet(kFloatLessThanOrEqual, node);
2673 VisitFloat64Compare(this, node, &cont);
2674 }
2675
VisitFloat64InsertLowWord32(Node * node)2676 void InstructionSelector::VisitFloat64InsertLowWord32(Node* node) {
2677 Arm64OperandGenerator g(this);
2678 Node* left = node->InputAt(0);
2679 Node* right = node->InputAt(1);
2680 if (left->opcode() == IrOpcode::kFloat64InsertHighWord32 &&
2681 CanCover(node, left)) {
2682 Node* right_of_left = left->InputAt(1);
2683 Emit(kArm64Bfi, g.DefineSameAsFirst(right), g.UseRegister(right),
2684 g.UseRegister(right_of_left), g.TempImmediate(32),
2685 g.TempImmediate(32));
2686 Emit(kArm64Float64MoveU64, g.DefineAsRegister(node), g.UseRegister(right));
2687 return;
2688 }
2689 Emit(kArm64Float64InsertLowWord32, g.DefineSameAsFirst(node),
2690 g.UseRegister(left), g.UseRegister(right));
2691 }
2692
2693
VisitFloat64InsertHighWord32(Node * node)2694 void InstructionSelector::VisitFloat64InsertHighWord32(Node* node) {
2695 Arm64OperandGenerator g(this);
2696 Node* left = node->InputAt(0);
2697 Node* right = node->InputAt(1);
2698 if (left->opcode() == IrOpcode::kFloat64InsertLowWord32 &&
2699 CanCover(node, left)) {
2700 Node* right_of_left = left->InputAt(1);
2701 Emit(kArm64Bfi, g.DefineSameAsFirst(left), g.UseRegister(right_of_left),
2702 g.UseRegister(right), g.TempImmediate(32), g.TempImmediate(32));
2703 Emit(kArm64Float64MoveU64, g.DefineAsRegister(node), g.UseRegister(left));
2704 return;
2705 }
2706 Emit(kArm64Float64InsertHighWord32, g.DefineSameAsFirst(node),
2707 g.UseRegister(left), g.UseRegister(right));
2708 }
2709
VisitWord32AtomicLoad(Node * node)2710 void InstructionSelector::VisitWord32AtomicLoad(Node* node) {
2711 LoadRepresentation load_rep = LoadRepresentationOf(node->op());
2712 ArchOpcode opcode = kArchNop;
2713 switch (load_rep.representation()) {
2714 case MachineRepresentation::kWord8:
2715 opcode =
2716 load_rep.IsSigned() ? kWord32AtomicLoadInt8 : kWord32AtomicLoadUint8;
2717 break;
2718 case MachineRepresentation::kWord16:
2719 opcode = load_rep.IsSigned() ? kWord32AtomicLoadInt16
2720 : kWord32AtomicLoadUint16;
2721 break;
2722 case MachineRepresentation::kWord32:
2723 opcode = kWord32AtomicLoadWord32;
2724 break;
2725 default:
2726 UNREACHABLE();
2727 return;
2728 }
2729 VisitAtomicLoad(this, node, opcode);
2730 }
2731
VisitWord64AtomicLoad(Node * node)2732 void InstructionSelector::VisitWord64AtomicLoad(Node* node) {
2733 LoadRepresentation load_rep = LoadRepresentationOf(node->op());
2734 ArchOpcode opcode = kArchNop;
2735 switch (load_rep.representation()) {
2736 case MachineRepresentation::kWord8:
2737 opcode = kArm64Word64AtomicLoadUint8;
2738 break;
2739 case MachineRepresentation::kWord16:
2740 opcode = kArm64Word64AtomicLoadUint16;
2741 break;
2742 case MachineRepresentation::kWord32:
2743 opcode = kArm64Word64AtomicLoadUint32;
2744 break;
2745 case MachineRepresentation::kWord64:
2746 opcode = kArm64Word64AtomicLoadUint64;
2747 break;
2748 default:
2749 UNREACHABLE();
2750 return;
2751 }
2752 VisitAtomicLoad(this, node, opcode);
2753 }
2754
VisitWord32AtomicStore(Node * node)2755 void InstructionSelector::VisitWord32AtomicStore(Node* node) {
2756 MachineRepresentation rep = AtomicStoreRepresentationOf(node->op());
2757 ArchOpcode opcode = kArchNop;
2758 switch (rep) {
2759 case MachineRepresentation::kWord8:
2760 opcode = kWord32AtomicStoreWord8;
2761 break;
2762 case MachineRepresentation::kWord16:
2763 opcode = kWord32AtomicStoreWord16;
2764 break;
2765 case MachineRepresentation::kWord32:
2766 opcode = kWord32AtomicStoreWord32;
2767 break;
2768 default:
2769 UNREACHABLE();
2770 return;
2771 }
2772 VisitAtomicStore(this, node, opcode);
2773 }
2774
VisitWord64AtomicStore(Node * node)2775 void InstructionSelector::VisitWord64AtomicStore(Node* node) {
2776 MachineRepresentation rep = AtomicStoreRepresentationOf(node->op());
2777 ArchOpcode opcode = kArchNop;
2778 switch (rep) {
2779 case MachineRepresentation::kWord8:
2780 opcode = kArm64Word64AtomicStoreWord8;
2781 break;
2782 case MachineRepresentation::kWord16:
2783 opcode = kArm64Word64AtomicStoreWord16;
2784 break;
2785 case MachineRepresentation::kWord32:
2786 opcode = kArm64Word64AtomicStoreWord32;
2787 break;
2788 case MachineRepresentation::kWord64:
2789 opcode = kArm64Word64AtomicStoreWord64;
2790 break;
2791 default:
2792 UNREACHABLE();
2793 return;
2794 }
2795 VisitAtomicStore(this, node, opcode);
2796 }
2797
VisitWord32AtomicExchange(Node * node)2798 void InstructionSelector::VisitWord32AtomicExchange(Node* node) {
2799 ArchOpcode opcode = kArchNop;
2800 MachineType type = AtomicOpType(node->op());
2801 if (type == MachineType::Int8()) {
2802 opcode = kWord32AtomicExchangeInt8;
2803 } else if (type == MachineType::Uint8()) {
2804 opcode = kWord32AtomicExchangeUint8;
2805 } else if (type == MachineType::Int16()) {
2806 opcode = kWord32AtomicExchangeInt16;
2807 } else if (type == MachineType::Uint16()) {
2808 opcode = kWord32AtomicExchangeUint16;
2809 } else if (type == MachineType::Int32() || type == MachineType::Uint32()) {
2810 opcode = kWord32AtomicExchangeWord32;
2811 } else {
2812 UNREACHABLE();
2813 return;
2814 }
2815 VisitAtomicExchange(this, node, opcode);
2816 }
2817
VisitWord64AtomicExchange(Node * node)2818 void InstructionSelector::VisitWord64AtomicExchange(Node* node) {
2819 ArchOpcode opcode = kArchNop;
2820 MachineType type = AtomicOpType(node->op());
2821 if (type == MachineType::Uint8()) {
2822 opcode = kArm64Word64AtomicExchangeUint8;
2823 } else if (type == MachineType::Uint16()) {
2824 opcode = kArm64Word64AtomicExchangeUint16;
2825 } else if (type == MachineType::Uint32()) {
2826 opcode = kArm64Word64AtomicExchangeUint32;
2827 } else if (type == MachineType::Uint64()) {
2828 opcode = kArm64Word64AtomicExchangeUint64;
2829 } else {
2830 UNREACHABLE();
2831 return;
2832 }
2833 VisitAtomicExchange(this, node, opcode);
2834 }
2835
VisitWord32AtomicCompareExchange(Node * node)2836 void InstructionSelector::VisitWord32AtomicCompareExchange(Node* node) {
2837 ArchOpcode opcode = kArchNop;
2838 MachineType type = AtomicOpType(node->op());
2839 if (type == MachineType::Int8()) {
2840 opcode = kWord32AtomicCompareExchangeInt8;
2841 } else if (type == MachineType::Uint8()) {
2842 opcode = kWord32AtomicCompareExchangeUint8;
2843 } else if (type == MachineType::Int16()) {
2844 opcode = kWord32AtomicCompareExchangeInt16;
2845 } else if (type == MachineType::Uint16()) {
2846 opcode = kWord32AtomicCompareExchangeUint16;
2847 } else if (type == MachineType::Int32() || type == MachineType::Uint32()) {
2848 opcode = kWord32AtomicCompareExchangeWord32;
2849 } else {
2850 UNREACHABLE();
2851 return;
2852 }
2853 VisitAtomicCompareExchange(this, node, opcode);
2854 }
2855
VisitWord64AtomicCompareExchange(Node * node)2856 void InstructionSelector::VisitWord64AtomicCompareExchange(Node* node) {
2857 ArchOpcode opcode = kArchNop;
2858 MachineType type = AtomicOpType(node->op());
2859 if (type == MachineType::Uint8()) {
2860 opcode = kArm64Word64AtomicCompareExchangeUint8;
2861 } else if (type == MachineType::Uint16()) {
2862 opcode = kArm64Word64AtomicCompareExchangeUint16;
2863 } else if (type == MachineType::Uint32()) {
2864 opcode = kArm64Word64AtomicCompareExchangeUint32;
2865 } else if (type == MachineType::Uint64()) {
2866 opcode = kArm64Word64AtomicCompareExchangeUint64;
2867 } else {
2868 UNREACHABLE();
2869 return;
2870 }
2871 VisitAtomicCompareExchange(this, node, opcode);
2872 }
2873
VisitWord32AtomicBinaryOperation(Node * node,ArchOpcode int8_op,ArchOpcode uint8_op,ArchOpcode int16_op,ArchOpcode uint16_op,ArchOpcode word32_op)2874 void InstructionSelector::VisitWord32AtomicBinaryOperation(
2875 Node* node, ArchOpcode int8_op, ArchOpcode uint8_op, ArchOpcode int16_op,
2876 ArchOpcode uint16_op, ArchOpcode word32_op) {
2877 ArchOpcode opcode = kArchNop;
2878 MachineType type = AtomicOpType(node->op());
2879 if (type == MachineType::Int8()) {
2880 opcode = int8_op;
2881 } else if (type == MachineType::Uint8()) {
2882 opcode = uint8_op;
2883 } else if (type == MachineType::Int16()) {
2884 opcode = int16_op;
2885 } else if (type == MachineType::Uint16()) {
2886 opcode = uint16_op;
2887 } else if (type == MachineType::Int32() || type == MachineType::Uint32()) {
2888 opcode = word32_op;
2889 } else {
2890 UNREACHABLE();
2891 return;
2892 }
2893 VisitAtomicBinop(this, node, opcode);
2894 }
2895
2896 #define VISIT_ATOMIC_BINOP(op) \
2897 void InstructionSelector::VisitWord32Atomic##op(Node* node) { \
2898 VisitWord32AtomicBinaryOperation( \
2899 node, kWord32Atomic##op##Int8, kWord32Atomic##op##Uint8, \
2900 kWord32Atomic##op##Int16, kWord32Atomic##op##Uint16, \
2901 kWord32Atomic##op##Word32); \
2902 }
2903 VISIT_ATOMIC_BINOP(Add)
VISIT_ATOMIC_BINOP(Sub)2904 VISIT_ATOMIC_BINOP(Sub)
2905 VISIT_ATOMIC_BINOP(And)
2906 VISIT_ATOMIC_BINOP(Or)
2907 VISIT_ATOMIC_BINOP(Xor)
2908 #undef VISIT_ATOMIC_BINOP
2909
2910 void InstructionSelector::VisitWord64AtomicBinaryOperation(
2911 Node* node, ArchOpcode uint8_op, ArchOpcode uint16_op, ArchOpcode uint32_op,
2912 ArchOpcode uint64_op) {
2913 ArchOpcode opcode = kArchNop;
2914 MachineType type = AtomicOpType(node->op());
2915 if (type == MachineType::Uint8()) {
2916 opcode = uint8_op;
2917 } else if (type == MachineType::Uint16()) {
2918 opcode = uint16_op;
2919 } else if (type == MachineType::Uint32()) {
2920 opcode = uint32_op;
2921 } else if (type == MachineType::Uint64()) {
2922 opcode = uint64_op;
2923 } else {
2924 UNREACHABLE();
2925 return;
2926 }
2927 VisitAtomicBinop(this, node, opcode);
2928 }
2929
2930 #define VISIT_ATOMIC_BINOP(op) \
2931 void InstructionSelector::VisitWord64Atomic##op(Node* node) { \
2932 VisitWord64AtomicBinaryOperation( \
2933 node, kArm64Word64Atomic##op##Uint8, kArm64Word64Atomic##op##Uint16, \
2934 kArm64Word64Atomic##op##Uint32, kArm64Word64Atomic##op##Uint64); \
2935 }
2936 VISIT_ATOMIC_BINOP(Add)
VISIT_ATOMIC_BINOP(Sub)2937 VISIT_ATOMIC_BINOP(Sub)
2938 VISIT_ATOMIC_BINOP(And)
2939 VISIT_ATOMIC_BINOP(Or)
2940 VISIT_ATOMIC_BINOP(Xor)
2941 #undef VISIT_ATOMIC_BINOP
2942
2943 void InstructionSelector::VisitInt32AbsWithOverflow(Node* node) {
2944 UNREACHABLE();
2945 }
2946
VisitInt64AbsWithOverflow(Node * node)2947 void InstructionSelector::VisitInt64AbsWithOverflow(Node* node) {
2948 UNREACHABLE();
2949 }
2950
2951 #define SIMD_TYPE_LIST(V) \
2952 V(F32x4) \
2953 V(I32x4) \
2954 V(I16x8) \
2955 V(I8x16)
2956
2957 #define SIMD_UNOP_LIST(V) \
2958 V(F32x4SConvertI32x4, kArm64F32x4SConvertI32x4) \
2959 V(F32x4UConvertI32x4, kArm64F32x4UConvertI32x4) \
2960 V(F32x4Abs, kArm64F32x4Abs) \
2961 V(F32x4Neg, kArm64F32x4Neg) \
2962 V(F32x4RecipApprox, kArm64F32x4RecipApprox) \
2963 V(F32x4RecipSqrtApprox, kArm64F32x4RecipSqrtApprox) \
2964 V(I32x4SConvertF32x4, kArm64I32x4SConvertF32x4) \
2965 V(I32x4SConvertI16x8Low, kArm64I32x4SConvertI16x8Low) \
2966 V(I32x4SConvertI16x8High, kArm64I32x4SConvertI16x8High) \
2967 V(I32x4Neg, kArm64I32x4Neg) \
2968 V(I32x4UConvertF32x4, kArm64I32x4UConvertF32x4) \
2969 V(I32x4UConvertI16x8Low, kArm64I32x4UConvertI16x8Low) \
2970 V(I32x4UConvertI16x8High, kArm64I32x4UConvertI16x8High) \
2971 V(I16x8SConvertI8x16Low, kArm64I16x8SConvertI8x16Low) \
2972 V(I16x8SConvertI8x16High, kArm64I16x8SConvertI8x16High) \
2973 V(I16x8Neg, kArm64I16x8Neg) \
2974 V(I16x8UConvertI8x16Low, kArm64I16x8UConvertI8x16Low) \
2975 V(I16x8UConvertI8x16High, kArm64I16x8UConvertI8x16High) \
2976 V(I8x16Neg, kArm64I8x16Neg) \
2977 V(S128Not, kArm64S128Not) \
2978 V(S1x4AnyTrue, kArm64S1x4AnyTrue) \
2979 V(S1x4AllTrue, kArm64S1x4AllTrue) \
2980 V(S1x8AnyTrue, kArm64S1x8AnyTrue) \
2981 V(S1x8AllTrue, kArm64S1x8AllTrue) \
2982 V(S1x16AnyTrue, kArm64S1x16AnyTrue) \
2983 V(S1x16AllTrue, kArm64S1x16AllTrue)
2984
2985 #define SIMD_SHIFT_OP_LIST(V) \
2986 V(I32x4Shl) \
2987 V(I32x4ShrS) \
2988 V(I32x4ShrU) \
2989 V(I16x8Shl) \
2990 V(I16x8ShrS) \
2991 V(I16x8ShrU) \
2992 V(I8x16Shl) \
2993 V(I8x16ShrS) \
2994 V(I8x16ShrU)
2995
2996 #define SIMD_BINOP_LIST(V) \
2997 V(F32x4Add, kArm64F32x4Add) \
2998 V(F32x4AddHoriz, kArm64F32x4AddHoriz) \
2999 V(F32x4Sub, kArm64F32x4Sub) \
3000 V(F32x4Mul, kArm64F32x4Mul) \
3001 V(F32x4Min, kArm64F32x4Min) \
3002 V(F32x4Max, kArm64F32x4Max) \
3003 V(F32x4Eq, kArm64F32x4Eq) \
3004 V(F32x4Ne, kArm64F32x4Ne) \
3005 V(F32x4Lt, kArm64F32x4Lt) \
3006 V(F32x4Le, kArm64F32x4Le) \
3007 V(I32x4Add, kArm64I32x4Add) \
3008 V(I32x4AddHoriz, kArm64I32x4AddHoriz) \
3009 V(I32x4Sub, kArm64I32x4Sub) \
3010 V(I32x4Mul, kArm64I32x4Mul) \
3011 V(I32x4MinS, kArm64I32x4MinS) \
3012 V(I32x4MaxS, kArm64I32x4MaxS) \
3013 V(I32x4Eq, kArm64I32x4Eq) \
3014 V(I32x4Ne, kArm64I32x4Ne) \
3015 V(I32x4GtS, kArm64I32x4GtS) \
3016 V(I32x4GeS, kArm64I32x4GeS) \
3017 V(I32x4MinU, kArm64I32x4MinU) \
3018 V(I32x4MaxU, kArm64I32x4MaxU) \
3019 V(I32x4GtU, kArm64I32x4GtU) \
3020 V(I32x4GeU, kArm64I32x4GeU) \
3021 V(I16x8SConvertI32x4, kArm64I16x8SConvertI32x4) \
3022 V(I16x8Add, kArm64I16x8Add) \
3023 V(I16x8AddSaturateS, kArm64I16x8AddSaturateS) \
3024 V(I16x8AddHoriz, kArm64I16x8AddHoriz) \
3025 V(I16x8Sub, kArm64I16x8Sub) \
3026 V(I16x8SubSaturateS, kArm64I16x8SubSaturateS) \
3027 V(I16x8Mul, kArm64I16x8Mul) \
3028 V(I16x8MinS, kArm64I16x8MinS) \
3029 V(I16x8MaxS, kArm64I16x8MaxS) \
3030 V(I16x8Eq, kArm64I16x8Eq) \
3031 V(I16x8Ne, kArm64I16x8Ne) \
3032 V(I16x8GtS, kArm64I16x8GtS) \
3033 V(I16x8GeS, kArm64I16x8GeS) \
3034 V(I16x8UConvertI32x4, kArm64I16x8UConvertI32x4) \
3035 V(I16x8AddSaturateU, kArm64I16x8AddSaturateU) \
3036 V(I16x8SubSaturateU, kArm64I16x8SubSaturateU) \
3037 V(I16x8MinU, kArm64I16x8MinU) \
3038 V(I16x8MaxU, kArm64I16x8MaxU) \
3039 V(I16x8GtU, kArm64I16x8GtU) \
3040 V(I16x8GeU, kArm64I16x8GeU) \
3041 V(I8x16SConvertI16x8, kArm64I8x16SConvertI16x8) \
3042 V(I8x16Add, kArm64I8x16Add) \
3043 V(I8x16AddSaturateS, kArm64I8x16AddSaturateS) \
3044 V(I8x16Sub, kArm64I8x16Sub) \
3045 V(I8x16SubSaturateS, kArm64I8x16SubSaturateS) \
3046 V(I8x16Mul, kArm64I8x16Mul) \
3047 V(I8x16MinS, kArm64I8x16MinS) \
3048 V(I8x16MaxS, kArm64I8x16MaxS) \
3049 V(I8x16Eq, kArm64I8x16Eq) \
3050 V(I8x16Ne, kArm64I8x16Ne) \
3051 V(I8x16GtS, kArm64I8x16GtS) \
3052 V(I8x16GeS, kArm64I8x16GeS) \
3053 V(I8x16UConvertI16x8, kArm64I8x16UConvertI16x8) \
3054 V(I8x16AddSaturateU, kArm64I8x16AddSaturateU) \
3055 V(I8x16SubSaturateU, kArm64I8x16SubSaturateU) \
3056 V(I8x16MinU, kArm64I8x16MinU) \
3057 V(I8x16MaxU, kArm64I8x16MaxU) \
3058 V(I8x16GtU, kArm64I8x16GtU) \
3059 V(I8x16GeU, kArm64I8x16GeU) \
3060 V(S128And, kArm64S128And) \
3061 V(S128Or, kArm64S128Or) \
3062 V(S128Xor, kArm64S128Xor)
3063
VisitS128Zero(Node * node)3064 void InstructionSelector::VisitS128Zero(Node* node) {
3065 Arm64OperandGenerator g(this);
3066 Emit(kArm64S128Zero, g.DefineAsRegister(node), g.DefineAsRegister(node));
3067 }
3068
3069 #define SIMD_VISIT_SPLAT(Type) \
3070 void InstructionSelector::Visit##Type##Splat(Node* node) { \
3071 VisitRR(this, kArm64##Type##Splat, node); \
3072 }
3073 SIMD_TYPE_LIST(SIMD_VISIT_SPLAT)
3074 #undef SIMD_VISIT_SPLAT
3075
3076 #define SIMD_VISIT_EXTRACT_LANE(Type) \
3077 void InstructionSelector::Visit##Type##ExtractLane(Node* node) { \
3078 VisitRRI(this, kArm64##Type##ExtractLane, node); \
3079 }
SIMD_TYPE_LIST(SIMD_VISIT_EXTRACT_LANE)3080 SIMD_TYPE_LIST(SIMD_VISIT_EXTRACT_LANE)
3081 #undef SIMD_VISIT_EXTRACT_LANE
3082
3083 #define SIMD_VISIT_REPLACE_LANE(Type) \
3084 void InstructionSelector::Visit##Type##ReplaceLane(Node* node) { \
3085 VisitRRIR(this, kArm64##Type##ReplaceLane, node); \
3086 }
3087 SIMD_TYPE_LIST(SIMD_VISIT_REPLACE_LANE)
3088 #undef SIMD_VISIT_REPLACE_LANE
3089 #undef SIMD_TYPE_LIST
3090
3091 #define SIMD_VISIT_UNOP(Name, instruction) \
3092 void InstructionSelector::Visit##Name(Node* node) { \
3093 VisitRR(this, instruction, node); \
3094 }
3095 SIMD_UNOP_LIST(SIMD_VISIT_UNOP)
3096 #undef SIMD_VISIT_UNOP
3097 #undef SIMD_UNOP_LIST
3098
3099 #define SIMD_VISIT_SHIFT_OP(Name) \
3100 void InstructionSelector::Visit##Name(Node* node) { \
3101 VisitRRI(this, kArm64##Name, node); \
3102 }
3103 SIMD_SHIFT_OP_LIST(SIMD_VISIT_SHIFT_OP)
3104 #undef SIMD_VISIT_SHIFT_OP
3105 #undef SIMD_SHIFT_OP_LIST
3106
3107 #define SIMD_VISIT_BINOP(Name, instruction) \
3108 void InstructionSelector::Visit##Name(Node* node) { \
3109 VisitRRR(this, instruction, node); \
3110 }
3111 SIMD_BINOP_LIST(SIMD_VISIT_BINOP)
3112 #undef SIMD_VISIT_BINOP
3113 #undef SIMD_BINOP_LIST
3114
3115 void InstructionSelector::VisitS128Select(Node* node) {
3116 Arm64OperandGenerator g(this);
3117 Emit(kArm64S128Select, g.DefineSameAsFirst(node),
3118 g.UseRegister(node->InputAt(0)), g.UseRegister(node->InputAt(1)),
3119 g.UseRegister(node->InputAt(2)));
3120 }
3121
3122 namespace {
3123
3124 struct ShuffleEntry {
3125 uint8_t shuffle[kSimd128Size];
3126 ArchOpcode opcode;
3127 };
3128
3129 static const ShuffleEntry arch_shuffles[] = {
3130 {{0, 1, 2, 3, 16, 17, 18, 19, 4, 5, 6, 7, 20, 21, 22, 23},
3131 kArm64S32x4ZipLeft},
3132 {{8, 9, 10, 11, 24, 25, 26, 27, 12, 13, 14, 15, 28, 29, 30, 31},
3133 kArm64S32x4ZipRight},
3134 {{0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27},
3135 kArm64S32x4UnzipLeft},
3136 {{4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23, 28, 29, 30, 31},
3137 kArm64S32x4UnzipRight},
3138 {{0, 1, 2, 3, 16, 17, 18, 19, 8, 9, 10, 11, 24, 25, 26, 27},
3139 kArm64S32x4TransposeLeft},
3140 {{4, 5, 6, 7, 20, 21, 22, 23, 12, 13, 14, 15, 21, 22, 23, 24},
3141 kArm64S32x4TransposeRight},
3142 {{4, 5, 6, 7, 0, 1, 2, 3, 12, 13, 14, 15, 8, 9, 10, 11},
3143 kArm64S32x2Reverse},
3144
3145 {{0, 1, 16, 17, 2, 3, 18, 19, 4, 5, 20, 21, 6, 7, 22, 23},
3146 kArm64S16x8ZipLeft},
3147 {{8, 9, 24, 25, 10, 11, 26, 27, 12, 13, 28, 29, 14, 15, 30, 31},
3148 kArm64S16x8ZipRight},
3149 {{0, 1, 4, 5, 8, 9, 12, 13, 16, 17, 20, 21, 24, 25, 28, 29},
3150 kArm64S16x8UnzipLeft},
3151 {{2, 3, 6, 7, 10, 11, 14, 15, 18, 19, 22, 23, 26, 27, 30, 31},
3152 kArm64S16x8UnzipRight},
3153 {{0, 1, 16, 17, 4, 5, 20, 21, 8, 9, 24, 25, 12, 13, 28, 29},
3154 kArm64S16x8TransposeLeft},
3155 {{2, 3, 18, 19, 6, 7, 22, 23, 10, 11, 26, 27, 14, 15, 30, 31},
3156 kArm64S16x8TransposeRight},
3157 {{6, 7, 4, 5, 2, 3, 0, 1, 14, 15, 12, 13, 10, 11, 8, 9},
3158 kArm64S16x4Reverse},
3159 {{2, 3, 0, 1, 6, 7, 4, 5, 10, 11, 8, 9, 14, 15, 12, 13},
3160 kArm64S16x2Reverse},
3161
3162 {{0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23},
3163 kArm64S8x16ZipLeft},
3164 {{8, 24, 9, 25, 10, 26, 11, 27, 12, 28, 13, 29, 14, 30, 15, 31},
3165 kArm64S8x16ZipRight},
3166 {{0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30},
3167 kArm64S8x16UnzipLeft},
3168 {{1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31},
3169 kArm64S8x16UnzipRight},
3170 {{0, 16, 2, 18, 4, 20, 6, 22, 8, 24, 10, 26, 12, 28, 14, 30},
3171 kArm64S8x16TransposeLeft},
3172 {{1, 17, 3, 19, 5, 21, 7, 23, 9, 25, 11, 27, 13, 29, 15, 31},
3173 kArm64S8x16TransposeRight},
3174 {{7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8}, kArm64S8x8Reverse},
3175 {{3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12}, kArm64S8x4Reverse},
3176 {{1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14},
3177 kArm64S8x2Reverse}};
3178
TryMatchArchShuffle(const uint8_t * shuffle,const ShuffleEntry * table,size_t num_entries,bool is_swizzle,ArchOpcode * opcode)3179 bool TryMatchArchShuffle(const uint8_t* shuffle, const ShuffleEntry* table,
3180 size_t num_entries, bool is_swizzle,
3181 ArchOpcode* opcode) {
3182 uint8_t mask = is_swizzle ? kSimd128Size - 1 : 2 * kSimd128Size - 1;
3183 for (size_t i = 0; i < num_entries; i++) {
3184 const ShuffleEntry& entry = table[i];
3185 int j = 0;
3186 for (; j < kSimd128Size; j++) {
3187 if ((entry.shuffle[j] & mask) != (shuffle[j] & mask)) {
3188 break;
3189 }
3190 }
3191 if (j == kSimd128Size) {
3192 *opcode = entry.opcode;
3193 return true;
3194 }
3195 }
3196 return false;
3197 }
3198
ArrangeShuffleTable(Arm64OperandGenerator * g,Node * input0,Node * input1,InstructionOperand * src0,InstructionOperand * src1)3199 void ArrangeShuffleTable(Arm64OperandGenerator* g, Node* input0, Node* input1,
3200 InstructionOperand* src0, InstructionOperand* src1) {
3201 if (input0 == input1) {
3202 // Unary, any q-register can be the table.
3203 *src0 = *src1 = g->UseRegister(input0);
3204 } else {
3205 // Binary, table registers must be consecutive.
3206 *src0 = g->UseFixed(input0, fp_fixed1);
3207 *src1 = g->UseFixed(input1, fp_fixed2);
3208 }
3209 }
3210
3211 } // namespace
3212
VisitS8x16Shuffle(Node * node)3213 void InstructionSelector::VisitS8x16Shuffle(Node* node) {
3214 uint8_t shuffle[kSimd128Size];
3215 bool is_swizzle;
3216 CanonicalizeShuffle(node, shuffle, &is_swizzle);
3217 uint8_t shuffle32x4[4];
3218 Arm64OperandGenerator g(this);
3219 ArchOpcode opcode;
3220 if (TryMatchArchShuffle(shuffle, arch_shuffles, arraysize(arch_shuffles),
3221 is_swizzle, &opcode)) {
3222 VisitRRR(this, opcode, node);
3223 return;
3224 }
3225 Node* input0 = node->InputAt(0);
3226 Node* input1 = node->InputAt(1);
3227 uint8_t offset;
3228 if (TryMatchConcat(shuffle, &offset)) {
3229 Emit(kArm64S8x16Concat, g.DefineAsRegister(node), g.UseRegister(input0),
3230 g.UseRegister(input1), g.UseImmediate(offset));
3231 return;
3232 }
3233 int index = 0;
3234 if (TryMatch32x4Shuffle(shuffle, shuffle32x4)) {
3235 if (TryMatchDup<4>(shuffle, &index)) {
3236 DCHECK_GT(4, index);
3237 Emit(kArm64S128Dup, g.DefineAsRegister(node), g.UseRegister(input0),
3238 g.UseImmediate(4), g.UseImmediate(index % 4));
3239 } else if (TryMatchIdentity(shuffle)) {
3240 EmitIdentity(node);
3241 } else {
3242 Emit(kArm64S32x4Shuffle, g.DefineAsRegister(node), g.UseRegister(input0),
3243 g.UseRegister(input1), g.UseImmediate(Pack4Lanes(shuffle32x4)));
3244 }
3245 return;
3246 }
3247 if (TryMatchDup<8>(shuffle, &index)) {
3248 DCHECK_GT(8, index);
3249 Emit(kArm64S128Dup, g.DefineAsRegister(node), g.UseRegister(input0),
3250 g.UseImmediate(8), g.UseImmediate(index % 8));
3251 return;
3252 }
3253 if (TryMatchDup<16>(shuffle, &index)) {
3254 DCHECK_GT(16, index);
3255 Emit(kArm64S128Dup, g.DefineAsRegister(node), g.UseRegister(input0),
3256 g.UseImmediate(16), g.UseImmediate(index % 16));
3257 return;
3258 }
3259 // Code generator uses vtbl, arrange sources to form a valid lookup table.
3260 InstructionOperand src0, src1;
3261 ArrangeShuffleTable(&g, input0, input1, &src0, &src1);
3262 Emit(kArm64S8x16Shuffle, g.DefineAsRegister(node), src0, src1,
3263 g.UseImmediate(Pack4Lanes(shuffle)),
3264 g.UseImmediate(Pack4Lanes(shuffle + 4)),
3265 g.UseImmediate(Pack4Lanes(shuffle + 8)),
3266 g.UseImmediate(Pack4Lanes(shuffle + 12)));
3267 }
3268
VisitSignExtendWord8ToInt32(Node * node)3269 void InstructionSelector::VisitSignExtendWord8ToInt32(Node* node) {
3270 VisitRR(this, kArm64Sxtb32, node);
3271 }
3272
VisitSignExtendWord16ToInt32(Node * node)3273 void InstructionSelector::VisitSignExtendWord16ToInt32(Node* node) {
3274 VisitRR(this, kArm64Sxth32, node);
3275 }
3276
VisitSignExtendWord8ToInt64(Node * node)3277 void InstructionSelector::VisitSignExtendWord8ToInt64(Node* node) {
3278 VisitRR(this, kArm64Sxtb, node);
3279 }
3280
VisitSignExtendWord16ToInt64(Node * node)3281 void InstructionSelector::VisitSignExtendWord16ToInt64(Node* node) {
3282 VisitRR(this, kArm64Sxth, node);
3283 }
3284
VisitSignExtendWord32ToInt64(Node * node)3285 void InstructionSelector::VisitSignExtendWord32ToInt64(Node* node) {
3286 VisitRR(this, kArm64Sxtw, node);
3287 }
3288
3289 // static
3290 MachineOperatorBuilder::Flags
SupportedMachineOperatorFlags()3291 InstructionSelector::SupportedMachineOperatorFlags() {
3292 return MachineOperatorBuilder::kFloat32RoundDown |
3293 MachineOperatorBuilder::kFloat64RoundDown |
3294 MachineOperatorBuilder::kFloat32RoundUp |
3295 MachineOperatorBuilder::kFloat64RoundUp |
3296 MachineOperatorBuilder::kFloat32RoundTruncate |
3297 MachineOperatorBuilder::kFloat64RoundTruncate |
3298 MachineOperatorBuilder::kFloat64RoundTiesAway |
3299 MachineOperatorBuilder::kFloat32RoundTiesEven |
3300 MachineOperatorBuilder::kFloat64RoundTiesEven |
3301 MachineOperatorBuilder::kWord32ShiftIsSafe |
3302 MachineOperatorBuilder::kInt32DivIsSafe |
3303 MachineOperatorBuilder::kUint32DivIsSafe |
3304 MachineOperatorBuilder::kWord32ReverseBits |
3305 MachineOperatorBuilder::kWord64ReverseBits |
3306 MachineOperatorBuilder::kSpeculationFence;
3307 }
3308
3309 // static
3310 MachineOperatorBuilder::AlignmentRequirements
AlignmentRequirements()3311 InstructionSelector::AlignmentRequirements() {
3312 return MachineOperatorBuilder::AlignmentRequirements::
3313 FullUnalignedAccessSupport();
3314 }
3315
3316 } // namespace compiler
3317 } // namespace internal
3318 } // namespace v8
3319