1 // Copyright 2014 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/base/bits.h"
6 #include "src/base/enum-set.h"
7 #include "src/base/iterator.h"
8 #include "src/base/platform/wrappers.h"
9 #include "src/codegen/machine-type.h"
10 #include "src/compiler/backend/instruction-selector-impl.h"
11 #include "src/compiler/node-matchers.h"
12 #include "src/compiler/node-properties.h"
13
14 namespace v8 {
15 namespace internal {
16 namespace compiler {
17
18 // Adds Arm-specific methods for generating InstructionOperands.
19 class ArmOperandGenerator : public OperandGenerator {
20 public:
ArmOperandGenerator(InstructionSelector * selector)21 explicit ArmOperandGenerator(InstructionSelector* selector)
22 : OperandGenerator(selector) {}
23
CanBeImmediate(int32_t value) const24 bool CanBeImmediate(int32_t value) const {
25 return Assembler::ImmediateFitsAddrMode1Instruction(value);
26 }
27
CanBeImmediate(uint32_t value) const28 bool CanBeImmediate(uint32_t value) const {
29 return CanBeImmediate(bit_cast<int32_t>(value));
30 }
31
CanBeImmediate(Node * node,InstructionCode opcode)32 bool CanBeImmediate(Node* node, InstructionCode opcode) {
33 Int32Matcher m(node);
34 if (!m.HasResolvedValue()) return false;
35 int32_t value = m.ResolvedValue();
36 switch (ArchOpcodeField::decode(opcode)) {
37 case kArmAnd:
38 case kArmMov:
39 case kArmMvn:
40 case kArmBic:
41 return CanBeImmediate(value) || CanBeImmediate(~value);
42
43 case kArmAdd:
44 case kArmSub:
45 case kArmCmp:
46 case kArmCmn:
47 return CanBeImmediate(value) || CanBeImmediate(-value);
48
49 case kArmTst:
50 case kArmTeq:
51 case kArmOrr:
52 case kArmEor:
53 case kArmRsb:
54 return CanBeImmediate(value);
55
56 case kArmVldrF32:
57 case kArmVstrF32:
58 case kArmVldrF64:
59 case kArmVstrF64:
60 return value >= -1020 && value <= 1020 && (value % 4) == 0;
61
62 case kArmLdrb:
63 case kArmLdrsb:
64 case kArmStrb:
65 case kArmLdr:
66 case kArmStr:
67 return value >= -4095 && value <= 4095;
68
69 case kArmLdrh:
70 case kArmLdrsh:
71 case kArmStrh:
72 return value >= -255 && value <= 255;
73
74 default:
75 break;
76 }
77 return false;
78 }
79 };
80
81 namespace {
82
VisitRR(InstructionSelector * selector,InstructionCode opcode,Node * node)83 void VisitRR(InstructionSelector* selector, InstructionCode opcode,
84 Node* node) {
85 ArmOperandGenerator g(selector);
86 selector->Emit(opcode, g.DefineAsRegister(node),
87 g.UseRegister(node->InputAt(0)));
88 }
89
VisitRRR(InstructionSelector * selector,InstructionCode opcode,Node * node)90 void VisitRRR(InstructionSelector* selector, InstructionCode opcode,
91 Node* node) {
92 ArmOperandGenerator g(selector);
93 selector->Emit(opcode, g.DefineAsRegister(node),
94 g.UseRegister(node->InputAt(0)),
95 g.UseRegister(node->InputAt(1)));
96 }
97
VisitSimdShiftRRR(InstructionSelector * selector,ArchOpcode opcode,Node * node,int width)98 void VisitSimdShiftRRR(InstructionSelector* selector, ArchOpcode opcode,
99 Node* node, int width) {
100 ArmOperandGenerator g(selector);
101 Int32Matcher m(node->InputAt(1));
102 if (m.HasResolvedValue()) {
103 if (m.IsMultipleOf(width)) {
104 selector->EmitIdentity(node);
105 } else {
106 selector->Emit(opcode, g.DefineAsRegister(node),
107 g.UseRegister(node->InputAt(0)),
108 g.UseImmediate(node->InputAt(1)));
109 }
110 } else {
111 VisitRRR(selector, opcode, node);
112 }
113 }
114
115 #if V8_ENABLE_WEBASSEMBLY
VisitRRRShuffle(InstructionSelector * selector,ArchOpcode opcode,Node * node)116 void VisitRRRShuffle(InstructionSelector* selector, ArchOpcode opcode,
117 Node* node) {
118 ArmOperandGenerator g(selector);
119 // Swap inputs to save an instruction in the CodeGenerator for High ops.
120 if (opcode == kArmS32x4ZipRight || opcode == kArmS32x4UnzipRight ||
121 opcode == kArmS32x4TransposeRight || opcode == kArmS16x8ZipRight ||
122 opcode == kArmS16x8UnzipRight || opcode == kArmS16x8TransposeRight ||
123 opcode == kArmS8x16ZipRight || opcode == kArmS8x16UnzipRight ||
124 opcode == kArmS8x16TransposeRight) {
125 Node* in0 = node->InputAt(0);
126 Node* in1 = node->InputAt(1);
127 node->ReplaceInput(0, in1);
128 node->ReplaceInput(1, in0);
129 }
130 // Use DefineSameAsFirst for binary ops that clobber their inputs, e.g. the
131 // NEON vzip, vuzp, and vtrn instructions.
132 selector->Emit(opcode, g.DefineSameAsFirst(node),
133 g.UseRegister(node->InputAt(0)),
134 g.UseRegister(node->InputAt(1)));
135 }
136 #endif // V8_ENABLE_WEBASSEMBLY
137
VisitRRI(InstructionSelector * selector,ArchOpcode opcode,Node * node)138 void VisitRRI(InstructionSelector* selector, ArchOpcode opcode, Node* node) {
139 ArmOperandGenerator g(selector);
140 int32_t imm = OpParameter<int32_t>(node->op());
141 selector->Emit(opcode, g.DefineAsRegister(node),
142 g.UseRegister(node->InputAt(0)), g.UseImmediate(imm));
143 }
144
VisitRRIR(InstructionSelector * selector,ArchOpcode opcode,Node * node)145 void VisitRRIR(InstructionSelector* selector, ArchOpcode opcode, Node* node) {
146 ArmOperandGenerator g(selector);
147 int32_t imm = OpParameter<int32_t>(node->op());
148 selector->Emit(opcode, g.DefineAsRegister(node),
149 g.UseRegister(node->InputAt(0)), g.UseImmediate(imm),
150 g.UseUniqueRegister(node->InputAt(1)));
151 }
152
153 template <IrOpcode::Value kOpcode, int kImmMin, int kImmMax,
154 AddressingMode kImmMode, AddressingMode kRegMode>
TryMatchShift(InstructionSelector * selector,InstructionCode * opcode_return,Node * node,InstructionOperand * value_return,InstructionOperand * shift_return)155 bool TryMatchShift(InstructionSelector* selector,
156 InstructionCode* opcode_return, Node* node,
157 InstructionOperand* value_return,
158 InstructionOperand* shift_return) {
159 ArmOperandGenerator g(selector);
160 if (node->opcode() == kOpcode) {
161 Int32BinopMatcher m(node);
162 *value_return = g.UseRegister(m.left().node());
163 if (m.right().IsInRange(kImmMin, kImmMax)) {
164 *opcode_return |= AddressingModeField::encode(kImmMode);
165 *shift_return = g.UseImmediate(m.right().node());
166 } else {
167 *opcode_return |= AddressingModeField::encode(kRegMode);
168 *shift_return = g.UseRegister(m.right().node());
169 }
170 return true;
171 }
172 return false;
173 }
174
175 template <IrOpcode::Value kOpcode, int kImmMin, int kImmMax,
176 AddressingMode kImmMode>
TryMatchShiftImmediate(InstructionSelector * selector,InstructionCode * opcode_return,Node * node,InstructionOperand * value_return,InstructionOperand * shift_return)177 bool TryMatchShiftImmediate(InstructionSelector* selector,
178 InstructionCode* opcode_return, Node* node,
179 InstructionOperand* value_return,
180 InstructionOperand* shift_return) {
181 ArmOperandGenerator g(selector);
182 if (node->opcode() == kOpcode) {
183 Int32BinopMatcher m(node);
184 if (m.right().IsInRange(kImmMin, kImmMax)) {
185 *opcode_return |= AddressingModeField::encode(kImmMode);
186 *value_return = g.UseRegister(m.left().node());
187 *shift_return = g.UseImmediate(m.right().node());
188 return true;
189 }
190 }
191 return false;
192 }
193
TryMatchROR(InstructionSelector * selector,InstructionCode * opcode_return,Node * node,InstructionOperand * value_return,InstructionOperand * shift_return)194 bool TryMatchROR(InstructionSelector* selector, InstructionCode* opcode_return,
195 Node* node, InstructionOperand* value_return,
196 InstructionOperand* shift_return) {
197 return TryMatchShift<IrOpcode::kWord32Ror, 1, 31, kMode_Operand2_R_ROR_I,
198 kMode_Operand2_R_ROR_R>(selector, opcode_return, node,
199 value_return, shift_return);
200 }
201
TryMatchASR(InstructionSelector * selector,InstructionCode * opcode_return,Node * node,InstructionOperand * value_return,InstructionOperand * shift_return)202 bool TryMatchASR(InstructionSelector* selector, InstructionCode* opcode_return,
203 Node* node, InstructionOperand* value_return,
204 InstructionOperand* shift_return) {
205 return TryMatchShift<IrOpcode::kWord32Sar, 1, 32, kMode_Operand2_R_ASR_I,
206 kMode_Operand2_R_ASR_R>(selector, opcode_return, node,
207 value_return, shift_return);
208 }
209
TryMatchLSL(InstructionSelector * selector,InstructionCode * opcode_return,Node * node,InstructionOperand * value_return,InstructionOperand * shift_return)210 bool TryMatchLSL(InstructionSelector* selector, InstructionCode* opcode_return,
211 Node* node, InstructionOperand* value_return,
212 InstructionOperand* shift_return) {
213 return TryMatchShift<IrOpcode::kWord32Shl, 0, 31, kMode_Operand2_R_LSL_I,
214 kMode_Operand2_R_LSL_R>(selector, opcode_return, node,
215 value_return, shift_return);
216 }
217
TryMatchLSLImmediate(InstructionSelector * selector,InstructionCode * opcode_return,Node * node,InstructionOperand * value_return,InstructionOperand * shift_return)218 bool TryMatchLSLImmediate(InstructionSelector* selector,
219 InstructionCode* opcode_return, Node* node,
220 InstructionOperand* value_return,
221 InstructionOperand* shift_return) {
222 return TryMatchShiftImmediate<IrOpcode::kWord32Shl, 0, 31,
223 kMode_Operand2_R_LSL_I>(
224 selector, opcode_return, node, value_return, shift_return);
225 }
226
TryMatchLSR(InstructionSelector * selector,InstructionCode * opcode_return,Node * node,InstructionOperand * value_return,InstructionOperand * shift_return)227 bool TryMatchLSR(InstructionSelector* selector, InstructionCode* opcode_return,
228 Node* node, InstructionOperand* value_return,
229 InstructionOperand* shift_return) {
230 return TryMatchShift<IrOpcode::kWord32Shr, 1, 32, kMode_Operand2_R_LSR_I,
231 kMode_Operand2_R_LSR_R>(selector, opcode_return, node,
232 value_return, shift_return);
233 }
234
TryMatchShift(InstructionSelector * selector,InstructionCode * opcode_return,Node * node,InstructionOperand * value_return,InstructionOperand * shift_return)235 bool TryMatchShift(InstructionSelector* selector,
236 InstructionCode* opcode_return, Node* node,
237 InstructionOperand* value_return,
238 InstructionOperand* shift_return) {
239 return (
240 TryMatchASR(selector, opcode_return, node, value_return, shift_return) ||
241 TryMatchLSL(selector, opcode_return, node, value_return, shift_return) ||
242 TryMatchLSR(selector, opcode_return, node, value_return, shift_return) ||
243 TryMatchROR(selector, opcode_return, node, value_return, shift_return));
244 }
245
TryMatchImmediateOrShift(InstructionSelector * selector,InstructionCode * opcode_return,Node * node,size_t * input_count_return,InstructionOperand * inputs)246 bool TryMatchImmediateOrShift(InstructionSelector* selector,
247 InstructionCode* opcode_return, Node* node,
248 size_t* input_count_return,
249 InstructionOperand* inputs) {
250 ArmOperandGenerator g(selector);
251 if (g.CanBeImmediate(node, *opcode_return)) {
252 *opcode_return |= AddressingModeField::encode(kMode_Operand2_I);
253 inputs[0] = g.UseImmediate(node);
254 *input_count_return = 1;
255 return true;
256 }
257 if (TryMatchShift(selector, opcode_return, node, &inputs[0], &inputs[1])) {
258 *input_count_return = 2;
259 return true;
260 }
261 return false;
262 }
263
VisitBinop(InstructionSelector * selector,Node * node,InstructionCode opcode,InstructionCode reverse_opcode,FlagsContinuation * cont)264 void VisitBinop(InstructionSelector* selector, Node* node,
265 InstructionCode opcode, InstructionCode reverse_opcode,
266 FlagsContinuation* cont) {
267 ArmOperandGenerator g(selector);
268 Int32BinopMatcher m(node);
269 InstructionOperand inputs[3];
270 size_t input_count = 0;
271 InstructionOperand outputs[1];
272 size_t output_count = 0;
273
274 if (m.left().node() == m.right().node()) {
275 // If both inputs refer to the same operand, enforce allocating a register
276 // for both of them to ensure that we don't end up generating code like
277 // this:
278 //
279 // mov r0, r1, asr #16
280 // adds r0, r0, r1, asr #16
281 // bvs label
282 InstructionOperand const input = g.UseRegister(m.left().node());
283 opcode |= AddressingModeField::encode(kMode_Operand2_R);
284 inputs[input_count++] = input;
285 inputs[input_count++] = input;
286 } else if (TryMatchImmediateOrShift(selector, &opcode, m.right().node(),
287 &input_count, &inputs[1])) {
288 inputs[0] = g.UseRegister(m.left().node());
289 input_count++;
290 } else if (TryMatchImmediateOrShift(selector, &reverse_opcode,
291 m.left().node(), &input_count,
292 &inputs[1])) {
293 inputs[0] = g.UseRegister(m.right().node());
294 opcode = reverse_opcode;
295 input_count++;
296 } else {
297 opcode |= AddressingModeField::encode(kMode_Operand2_R);
298 inputs[input_count++] = g.UseRegister(m.left().node());
299 inputs[input_count++] = g.UseRegister(m.right().node());
300 }
301
302 outputs[output_count++] = g.DefineAsRegister(node);
303
304 DCHECK_NE(0u, input_count);
305 DCHECK_EQ(1u, output_count);
306 DCHECK_GE(arraysize(inputs), input_count);
307 DCHECK_GE(arraysize(outputs), output_count);
308 DCHECK_NE(kMode_None, AddressingModeField::decode(opcode));
309
310 selector->EmitWithContinuation(opcode, output_count, outputs, input_count,
311 inputs, cont);
312 }
313
VisitBinop(InstructionSelector * selector,Node * node,InstructionCode opcode,InstructionCode reverse_opcode)314 void VisitBinop(InstructionSelector* selector, Node* node,
315 InstructionCode opcode, InstructionCode reverse_opcode) {
316 FlagsContinuation cont;
317 VisitBinop(selector, node, opcode, reverse_opcode, &cont);
318 }
319
EmitDiv(InstructionSelector * selector,ArchOpcode div_opcode,ArchOpcode f64i32_opcode,ArchOpcode i32f64_opcode,InstructionOperand result_operand,InstructionOperand left_operand,InstructionOperand right_operand)320 void EmitDiv(InstructionSelector* selector, ArchOpcode div_opcode,
321 ArchOpcode f64i32_opcode, ArchOpcode i32f64_opcode,
322 InstructionOperand result_operand, InstructionOperand left_operand,
323 InstructionOperand right_operand) {
324 ArmOperandGenerator g(selector);
325 if (selector->IsSupported(SUDIV)) {
326 selector->Emit(div_opcode, result_operand, left_operand, right_operand);
327 return;
328 }
329 InstructionOperand left_double_operand = g.TempDoubleRegister();
330 InstructionOperand right_double_operand = g.TempDoubleRegister();
331 InstructionOperand result_double_operand = g.TempDoubleRegister();
332 selector->Emit(f64i32_opcode, left_double_operand, left_operand);
333 selector->Emit(f64i32_opcode, right_double_operand, right_operand);
334 selector->Emit(kArmVdivF64, result_double_operand, left_double_operand,
335 right_double_operand);
336 selector->Emit(i32f64_opcode, result_operand, result_double_operand);
337 }
338
VisitDiv(InstructionSelector * selector,Node * node,ArchOpcode div_opcode,ArchOpcode f64i32_opcode,ArchOpcode i32f64_opcode)339 void VisitDiv(InstructionSelector* selector, Node* node, ArchOpcode div_opcode,
340 ArchOpcode f64i32_opcode, ArchOpcode i32f64_opcode) {
341 ArmOperandGenerator g(selector);
342 Int32BinopMatcher m(node);
343 EmitDiv(selector, div_opcode, f64i32_opcode, i32f64_opcode,
344 g.DefineAsRegister(node), g.UseRegister(m.left().node()),
345 g.UseRegister(m.right().node()));
346 }
347
VisitMod(InstructionSelector * selector,Node * node,ArchOpcode div_opcode,ArchOpcode f64i32_opcode,ArchOpcode i32f64_opcode)348 void VisitMod(InstructionSelector* selector, Node* node, ArchOpcode div_opcode,
349 ArchOpcode f64i32_opcode, ArchOpcode i32f64_opcode) {
350 ArmOperandGenerator g(selector);
351 Int32BinopMatcher m(node);
352 InstructionOperand div_operand = g.TempRegister();
353 InstructionOperand result_operand = g.DefineAsRegister(node);
354 InstructionOperand left_operand = g.UseRegister(m.left().node());
355 InstructionOperand right_operand = g.UseRegister(m.right().node());
356 EmitDiv(selector, div_opcode, f64i32_opcode, i32f64_opcode, div_operand,
357 left_operand, right_operand);
358 if (selector->IsSupported(ARMv7)) {
359 selector->Emit(kArmMls, result_operand, div_operand, right_operand,
360 left_operand);
361 } else {
362 InstructionOperand mul_operand = g.TempRegister();
363 selector->Emit(kArmMul, mul_operand, div_operand, right_operand);
364 selector->Emit(kArmSub | AddressingModeField::encode(kMode_Operand2_R),
365 result_operand, left_operand, mul_operand);
366 }
367 }
368
369 // Adds the base and offset into a register, then change the addressing
370 // mode of opcode_return to use this register. Certain instructions, e.g.
371 // vld1 and vst1, when given two registers, will post-increment the offset, i.e.
372 // perform the operation at base, then add offset to base. What we intend is to
373 // access at (base+offset).
EmitAddBeforeS128LoadStore(InstructionSelector * selector,InstructionCode * opcode_return,size_t * input_count_return,InstructionOperand * inputs)374 void EmitAddBeforeS128LoadStore(InstructionSelector* selector,
375 InstructionCode* opcode_return,
376 size_t* input_count_return,
377 InstructionOperand* inputs) {
378 ArmOperandGenerator g(selector);
379 InstructionOperand addr = g.TempRegister();
380 InstructionCode op = kArmAdd;
381 op |= AddressingModeField::encode(kMode_Operand2_R);
382 selector->Emit(op, 1, &addr, 2, inputs);
383 *opcode_return |= AddressingModeField::encode(kMode_Operand2_R);
384 *input_count_return -= 1;
385 inputs[0] = addr;
386 }
387
EmitLoad(InstructionSelector * selector,InstructionCode opcode,InstructionOperand * output,Node * base,Node * index)388 void EmitLoad(InstructionSelector* selector, InstructionCode opcode,
389 InstructionOperand* output, Node* base, Node* index) {
390 ArmOperandGenerator g(selector);
391 InstructionOperand inputs[3];
392 size_t input_count = 2;
393
394 ExternalReferenceMatcher m(base);
395 if (m.HasResolvedValue() &&
396 selector->CanAddressRelativeToRootsRegister(m.ResolvedValue())) {
397 Int32Matcher int_matcher(index);
398 if (int_matcher.HasResolvedValue()) {
399 ptrdiff_t const delta =
400 int_matcher.ResolvedValue() +
401 TurboAssemblerBase::RootRegisterOffsetForExternalReference(
402 selector->isolate(), m.ResolvedValue());
403 input_count = 1;
404 inputs[0] = g.UseImmediate(static_cast<int32_t>(delta));
405 opcode |= AddressingModeField::encode(kMode_Root);
406 selector->Emit(opcode, 1, output, input_count, inputs);
407 return;
408 }
409 }
410
411 inputs[0] = g.UseRegister(base);
412 if (g.CanBeImmediate(index, opcode)) {
413 inputs[1] = g.UseImmediate(index);
414 opcode |= AddressingModeField::encode(kMode_Offset_RI);
415 } else if ((opcode == kArmLdr) &&
416 TryMatchLSLImmediate(selector, &opcode, index, &inputs[1],
417 &inputs[2])) {
418 input_count = 3;
419 } else {
420 inputs[1] = g.UseRegister(index);
421 if (opcode == kArmVld1S128) {
422 EmitAddBeforeS128LoadStore(selector, &opcode, &input_count, &inputs[0]);
423 } else {
424 opcode |= AddressingModeField::encode(kMode_Offset_RR);
425 }
426 }
427 selector->Emit(opcode, 1, output, input_count, inputs);
428 }
429
EmitStore(InstructionSelector * selector,InstructionCode opcode,size_t input_count,InstructionOperand * inputs,Node * index)430 void EmitStore(InstructionSelector* selector, InstructionCode opcode,
431 size_t input_count, InstructionOperand* inputs, Node* index) {
432 ArmOperandGenerator g(selector);
433 ArchOpcode arch_opcode = ArchOpcodeField::decode(opcode);
434
435 if (g.CanBeImmediate(index, opcode)) {
436 inputs[input_count++] = g.UseImmediate(index);
437 opcode |= AddressingModeField::encode(kMode_Offset_RI);
438 } else if ((arch_opcode == kArmStr || arch_opcode == kAtomicStoreWord32) &&
439 TryMatchLSLImmediate(selector, &opcode, index, &inputs[2],
440 &inputs[3])) {
441 input_count = 4;
442 } else {
443 inputs[input_count++] = g.UseRegister(index);
444 if (arch_opcode == kArmVst1S128) {
445 // Inputs are value, base, index, only care about base and index.
446 EmitAddBeforeS128LoadStore(selector, &opcode, &input_count, &inputs[1]);
447 } else {
448 opcode |= AddressingModeField::encode(kMode_Offset_RR);
449 }
450 }
451 selector->Emit(opcode, 0, nullptr, input_count, inputs);
452 }
453
VisitPairAtomicBinOp(InstructionSelector * selector,Node * node,ArchOpcode opcode)454 void VisitPairAtomicBinOp(InstructionSelector* selector, Node* node,
455 ArchOpcode opcode) {
456 ArmOperandGenerator g(selector);
457 Node* base = node->InputAt(0);
458 Node* index = node->InputAt(1);
459 Node* value = node->InputAt(2);
460 Node* value_high = node->InputAt(3);
461 AddressingMode addressing_mode = kMode_Offset_RR;
462 InstructionCode code = opcode | AddressingModeField::encode(addressing_mode);
463 InstructionOperand inputs[] = {
464 g.UseUniqueRegister(value), g.UseUniqueRegister(value_high),
465 g.UseUniqueRegister(base), g.UseUniqueRegister(index)};
466 InstructionOperand outputs[2];
467 size_t output_count = 0;
468 InstructionOperand temps[6];
469 size_t temp_count = 0;
470 temps[temp_count++] = g.TempRegister();
471 temps[temp_count++] = g.TempRegister(r6);
472 temps[temp_count++] = g.TempRegister(r7);
473 temps[temp_count++] = g.TempRegister();
474 Node* projection0 = NodeProperties::FindProjection(node, 0);
475 Node* projection1 = NodeProperties::FindProjection(node, 1);
476 if (projection0) {
477 outputs[output_count++] = g.DefineAsFixed(projection0, r2);
478 } else {
479 temps[temp_count++] = g.TempRegister(r2);
480 }
481 if (projection1) {
482 outputs[output_count++] = g.DefineAsFixed(projection1, r3);
483 } else {
484 temps[temp_count++] = g.TempRegister(r3);
485 }
486 selector->Emit(code, output_count, outputs, arraysize(inputs), inputs,
487 temp_count, temps);
488 }
489
490 } // namespace
491
VisitStackSlot(Node * node)492 void InstructionSelector::VisitStackSlot(Node* node) {
493 StackSlotRepresentation rep = StackSlotRepresentationOf(node->op());
494 int slot = frame_->AllocateSpillSlot(rep.size(), rep.alignment());
495 OperandGenerator g(this);
496
497 Emit(kArchStackSlot, g.DefineAsRegister(node),
498 sequence()->AddImmediate(Constant(slot)), 0, nullptr);
499 }
500
VisitAbortCSADcheck(Node * node)501 void InstructionSelector::VisitAbortCSADcheck(Node* node) {
502 ArmOperandGenerator g(this);
503 Emit(kArchAbortCSADcheck, g.NoOutput(), g.UseFixed(node->InputAt(0), r1));
504 }
505
VisitStoreLane(Node * node)506 void InstructionSelector::VisitStoreLane(Node* node) {
507 StoreLaneParameters params = StoreLaneParametersOf(node->op());
508 LoadStoreLaneParams f(params.rep, params.laneidx);
509 InstructionCode opcode =
510 f.low_op ? kArmS128StoreLaneLow : kArmS128StoreLaneHigh;
511 opcode |= MiscField::encode(f.sz);
512
513 ArmOperandGenerator g(this);
514 InstructionOperand inputs[4];
515 size_t input_count = 4;
516 inputs[0] = g.UseRegister(node->InputAt(2));
517 inputs[1] = g.UseImmediate(f.laneidx);
518 inputs[2] = g.UseRegister(node->InputAt(0));
519 inputs[3] = g.UseRegister(node->InputAt(1));
520 EmitAddBeforeS128LoadStore(this, &opcode, &input_count, &inputs[2]);
521 Emit(opcode, 0, nullptr, input_count, inputs);
522 }
523
VisitLoadLane(Node * node)524 void InstructionSelector::VisitLoadLane(Node* node) {
525 LoadLaneParameters params = LoadLaneParametersOf(node->op());
526 LoadStoreLaneParams f(params.rep.representation(), params.laneidx);
527 InstructionCode opcode =
528 f.low_op ? kArmS128LoadLaneLow : kArmS128LoadLaneHigh;
529 opcode |= MiscField::encode(f.sz);
530
531 ArmOperandGenerator g(this);
532 InstructionOperand output = g.DefineSameAsFirst(node);
533 InstructionOperand inputs[4];
534 size_t input_count = 4;
535 inputs[0] = g.UseRegister(node->InputAt(2));
536 inputs[1] = g.UseImmediate(f.laneidx);
537 inputs[2] = g.UseRegister(node->InputAt(0));
538 inputs[3] = g.UseRegister(node->InputAt(1));
539 EmitAddBeforeS128LoadStore(this, &opcode, &input_count, &inputs[2]);
540 Emit(opcode, 1, &output, input_count, inputs);
541 }
542
VisitLoadTransform(Node * node)543 void InstructionSelector::VisitLoadTransform(Node* node) {
544 LoadTransformParameters params = LoadTransformParametersOf(node->op());
545 InstructionCode opcode = kArchNop;
546 switch (params.transformation) {
547 case LoadTransformation::kS128Load8Splat:
548 opcode = kArmS128Load8Splat;
549 break;
550 case LoadTransformation::kS128Load16Splat:
551 opcode = kArmS128Load16Splat;
552 break;
553 case LoadTransformation::kS128Load32Splat:
554 opcode = kArmS128Load32Splat;
555 break;
556 case LoadTransformation::kS128Load64Splat:
557 opcode = kArmS128Load64Splat;
558 break;
559 case LoadTransformation::kS128Load8x8S:
560 opcode = kArmS128Load8x8S;
561 break;
562 case LoadTransformation::kS128Load8x8U:
563 opcode = kArmS128Load8x8U;
564 break;
565 case LoadTransformation::kS128Load16x4S:
566 opcode = kArmS128Load16x4S;
567 break;
568 case LoadTransformation::kS128Load16x4U:
569 opcode = kArmS128Load16x4U;
570 break;
571 case LoadTransformation::kS128Load32x2S:
572 opcode = kArmS128Load32x2S;
573 break;
574 case LoadTransformation::kS128Load32x2U:
575 opcode = kArmS128Load32x2U;
576 break;
577 case LoadTransformation::kS128Load32Zero:
578 opcode = kArmS128Load32Zero;
579 break;
580 case LoadTransformation::kS128Load64Zero:
581 opcode = kArmS128Load64Zero;
582 break;
583 default:
584 UNIMPLEMENTED();
585 }
586
587 ArmOperandGenerator g(this);
588 InstructionOperand output = g.DefineAsRegister(node);
589 InstructionOperand inputs[2];
590 size_t input_count = 2;
591 inputs[0] = g.UseRegister(node->InputAt(0));
592 inputs[1] = g.UseRegister(node->InputAt(1));
593 EmitAddBeforeS128LoadStore(this, &opcode, &input_count, &inputs[0]);
594 Emit(opcode, 1, &output, input_count, inputs);
595 }
596
VisitLoad(Node * node)597 void InstructionSelector::VisitLoad(Node* node) {
598 LoadRepresentation load_rep = LoadRepresentationOf(node->op());
599 ArmOperandGenerator g(this);
600 Node* base = node->InputAt(0);
601 Node* index = node->InputAt(1);
602
603 InstructionCode opcode = kArchNop;
604 switch (load_rep.representation()) {
605 case MachineRepresentation::kFloat32:
606 opcode = kArmVldrF32;
607 break;
608 case MachineRepresentation::kFloat64:
609 opcode = kArmVldrF64;
610 break;
611 case MachineRepresentation::kBit: // Fall through.
612 case MachineRepresentation::kWord8:
613 opcode = load_rep.IsUnsigned() ? kArmLdrb : kArmLdrsb;
614 break;
615 case MachineRepresentation::kWord16:
616 opcode = load_rep.IsUnsigned() ? kArmLdrh : kArmLdrsh;
617 break;
618 case MachineRepresentation::kTaggedSigned: // Fall through.
619 case MachineRepresentation::kTaggedPointer: // Fall through.
620 case MachineRepresentation::kTagged: // Fall through.
621 case MachineRepresentation::kWord32:
622 opcode = kArmLdr;
623 break;
624 case MachineRepresentation::kSimd128:
625 opcode = kArmVld1S128;
626 break;
627 case MachineRepresentation::kCompressedPointer: // Fall through.
628 case MachineRepresentation::kCompressed: // Fall through.
629 case MachineRepresentation::kSandboxedPointer: // Fall through.
630 case MachineRepresentation::kWord64: // Fall through.
631 case MachineRepresentation::kMapWord: // Fall through.
632 case MachineRepresentation::kNone:
633 UNREACHABLE();
634 }
635
636 InstructionOperand output = g.DefineAsRegister(node);
637 EmitLoad(this, opcode, &output, base, index);
638 }
639
VisitProtectedLoad(Node * node)640 void InstructionSelector::VisitProtectedLoad(Node* node) {
641 // TODO(eholk)
642 UNIMPLEMENTED();
643 }
644
645 namespace {
646
GetStoreOpcode(MachineRepresentation rep)647 ArchOpcode GetStoreOpcode(MachineRepresentation rep) {
648 switch (rep) {
649 case MachineRepresentation::kFloat32:
650 return kArmVstrF32;
651 case MachineRepresentation::kFloat64:
652 return kArmVstrF64;
653 case MachineRepresentation::kBit: // Fall through.
654 case MachineRepresentation::kWord8:
655 return kArmStrb;
656 case MachineRepresentation::kWord16:
657 return kArmStrh;
658 case MachineRepresentation::kTaggedSigned: // Fall through.
659 case MachineRepresentation::kTaggedPointer: // Fall through.
660 case MachineRepresentation::kTagged: // Fall through.
661 case MachineRepresentation::kWord32:
662 return kArmStr;
663 case MachineRepresentation::kSimd128:
664 return kArmVst1S128;
665 case MachineRepresentation::kCompressedPointer: // Fall through.
666 case MachineRepresentation::kCompressed: // Fall through.
667 case MachineRepresentation::kSandboxedPointer: // Fall through.
668 case MachineRepresentation::kWord64: // Fall through.
669 case MachineRepresentation::kMapWord: // Fall through.
670 case MachineRepresentation::kNone:
671 UNREACHABLE();
672 }
673 }
674
GetAtomicStoreOpcode(MachineRepresentation rep)675 ArchOpcode GetAtomicStoreOpcode(MachineRepresentation rep) {
676 switch (rep) {
677 case MachineRepresentation::kWord8:
678 return kAtomicStoreWord8;
679 case MachineRepresentation::kWord16:
680 return kAtomicStoreWord16;
681 case MachineRepresentation::kTaggedSigned: // Fall through.
682 case MachineRepresentation::kTaggedPointer: // Fall through.
683 case MachineRepresentation::kTagged: // Fall through.
684 case MachineRepresentation::kWord32:
685 return kAtomicStoreWord32;
686 default:
687 UNREACHABLE();
688 }
689 }
690
VisitStoreCommon(InstructionSelector * selector,Node * node,StoreRepresentation store_rep,base::Optional<AtomicMemoryOrder> atomic_order)691 void VisitStoreCommon(InstructionSelector* selector, Node* node,
692 StoreRepresentation store_rep,
693 base::Optional<AtomicMemoryOrder> atomic_order) {
694 ArmOperandGenerator g(selector);
695 Node* base = node->InputAt(0);
696 Node* index = node->InputAt(1);
697 Node* value = node->InputAt(2);
698
699 WriteBarrierKind write_barrier_kind = store_rep.write_barrier_kind();
700 MachineRepresentation rep = store_rep.representation();
701
702 if (FLAG_enable_unconditional_write_barriers && CanBeTaggedPointer(rep)) {
703 write_barrier_kind = kFullWriteBarrier;
704 }
705
706 if (write_barrier_kind != kNoWriteBarrier && !FLAG_disable_write_barriers) {
707 DCHECK(CanBeTaggedPointer(rep));
708 AddressingMode addressing_mode;
709 InstructionOperand inputs[3];
710 size_t input_count = 0;
711 inputs[input_count++] = g.UseUniqueRegister(base);
712 // OutOfLineRecordWrite uses the index in an 'add' instruction as well as
713 // for the store itself, so we must check compatibility with both.
714 if (g.CanBeImmediate(index, kArmAdd) && g.CanBeImmediate(index, kArmStr)) {
715 inputs[input_count++] = g.UseImmediate(index);
716 addressing_mode = kMode_Offset_RI;
717 } else {
718 inputs[input_count++] = g.UseUniqueRegister(index);
719 addressing_mode = kMode_Offset_RR;
720 }
721 inputs[input_count++] = g.UseUniqueRegister(value);
722 RecordWriteMode record_write_mode =
723 WriteBarrierKindToRecordWriteMode(write_barrier_kind);
724 InstructionCode code;
725 if (!atomic_order) {
726 code = kArchStoreWithWriteBarrier;
727 code |= MiscField::encode(static_cast<int>(record_write_mode));
728 } else {
729 code = kArchAtomicStoreWithWriteBarrier;
730 code |= AtomicMemoryOrderField::encode(*atomic_order);
731 code |= AtomicStoreRecordWriteModeField::encode(record_write_mode);
732 }
733 code |= AddressingModeField::encode(addressing_mode);
734 selector->Emit(code, 0, nullptr, input_count, inputs);
735 } else {
736 InstructionCode opcode = kArchNop;
737 if (!atomic_order) {
738 opcode = GetStoreOpcode(rep);
739 } else {
740 // Release stores emit DMB ISH; STR while sequentially consistent stores
741 // emit DMB ISH; STR; DMB ISH.
742 // https://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html
743 opcode = GetAtomicStoreOpcode(rep);
744 opcode |= AtomicMemoryOrderField::encode(*atomic_order);
745 }
746
747 ExternalReferenceMatcher m(base);
748 if (m.HasResolvedValue() &&
749 selector->CanAddressRelativeToRootsRegister(m.ResolvedValue())) {
750 Int32Matcher int_matcher(index);
751 if (int_matcher.HasResolvedValue()) {
752 ptrdiff_t const delta =
753 int_matcher.ResolvedValue() +
754 TurboAssemblerBase::RootRegisterOffsetForExternalReference(
755 selector->isolate(), m.ResolvedValue());
756 int input_count = 2;
757 InstructionOperand inputs[2];
758 inputs[0] = g.UseRegister(value);
759 inputs[1] = g.UseImmediate(static_cast<int32_t>(delta));
760 opcode |= AddressingModeField::encode(kMode_Root);
761 selector->Emit(opcode, 0, nullptr, input_count, inputs);
762 return;
763 }
764 }
765
766 InstructionOperand inputs[4];
767 size_t input_count = 0;
768 inputs[input_count++] = g.UseRegister(value);
769 inputs[input_count++] = g.UseRegister(base);
770 EmitStore(selector, opcode, input_count, inputs, index);
771 }
772 }
773
774 } // namespace
775
VisitStore(Node * node)776 void InstructionSelector::VisitStore(Node* node) {
777 VisitStoreCommon(this, node, StoreRepresentationOf(node->op()),
778 base::nullopt);
779 }
780
VisitProtectedStore(Node * node)781 void InstructionSelector::VisitProtectedStore(Node* node) {
782 // TODO(eholk)
783 UNIMPLEMENTED();
784 }
785
VisitUnalignedLoad(Node * node)786 void InstructionSelector::VisitUnalignedLoad(Node* node) {
787 MachineRepresentation load_rep =
788 LoadRepresentationOf(node->op()).representation();
789 ArmOperandGenerator g(this);
790 Node* base = node->InputAt(0);
791 Node* index = node->InputAt(1);
792
793 InstructionCode opcode = kArmLdr;
794 // Only floating point loads need to be specially handled; integer loads
795 // support unaligned access. We support unaligned FP loads by loading to
796 // integer registers first, then moving to the destination FP register. If
797 // NEON is supported, we use the vld1.8 instruction.
798 switch (load_rep) {
799 case MachineRepresentation::kFloat32: {
800 InstructionOperand temp = g.TempRegister();
801 EmitLoad(this, opcode, &temp, base, index);
802 Emit(kArmVmovF32U32, g.DefineAsRegister(node), temp);
803 return;
804 }
805 case MachineRepresentation::kFloat64: {
806 // Compute the address of the least-significant byte of the FP value.
807 // We assume that the base node is unlikely to be an encodable immediate
808 // or the result of a shift operation, so only consider the addressing
809 // mode that should be used for the index node.
810 InstructionCode add_opcode = kArmAdd;
811 InstructionOperand inputs[3];
812 inputs[0] = g.UseRegister(base);
813
814 size_t input_count;
815 if (TryMatchImmediateOrShift(this, &add_opcode, index, &input_count,
816 &inputs[1])) {
817 // input_count has been set by TryMatchImmediateOrShift(), so
818 // increment it to account for the base register in inputs[0].
819 input_count++;
820 } else {
821 add_opcode |= AddressingModeField::encode(kMode_Operand2_R);
822 inputs[1] = g.UseRegister(index);
823 input_count = 2; // Base register and index.
824 }
825
826 InstructionOperand addr = g.TempRegister();
827 Emit(add_opcode, 1, &addr, input_count, inputs);
828
829 if (CpuFeatures::IsSupported(NEON)) {
830 // With NEON we can load directly from the calculated address.
831 InstructionCode op = kArmVld1F64;
832 op |= AddressingModeField::encode(kMode_Operand2_R);
833 Emit(op, g.DefineAsRegister(node), addr);
834 } else {
835 // Load both halves and move to an FP register.
836 InstructionOperand fp_lo = g.TempRegister();
837 InstructionOperand fp_hi = g.TempRegister();
838 opcode |= AddressingModeField::encode(kMode_Offset_RI);
839 Emit(opcode, fp_lo, addr, g.TempImmediate(0));
840 Emit(opcode, fp_hi, addr, g.TempImmediate(4));
841 Emit(kArmVmovF64U32U32, g.DefineAsRegister(node), fp_lo, fp_hi);
842 }
843 return;
844 }
845 default:
846 // All other cases should support unaligned accesses.
847 UNREACHABLE();
848 }
849 }
850
VisitUnalignedStore(Node * node)851 void InstructionSelector::VisitUnalignedStore(Node* node) {
852 ArmOperandGenerator g(this);
853 Node* base = node->InputAt(0);
854 Node* index = node->InputAt(1);
855 Node* value = node->InputAt(2);
856
857 InstructionOperand inputs[4];
858 size_t input_count = 0;
859
860 UnalignedStoreRepresentation store_rep =
861 UnalignedStoreRepresentationOf(node->op());
862
863 // Only floating point stores need to be specially handled; integer stores
864 // support unaligned access. We support unaligned FP stores by moving the
865 // value to integer registers first, then storing to the destination address.
866 // If NEON is supported, we use the vst1.8 instruction.
867 switch (store_rep) {
868 case MachineRepresentation::kFloat32: {
869 inputs[input_count++] = g.TempRegister();
870 Emit(kArmVmovU32F32, inputs[0], g.UseRegister(value));
871 inputs[input_count++] = g.UseRegister(base);
872 EmitStore(this, kArmStr, input_count, inputs, index);
873 return;
874 }
875 case MachineRepresentation::kFloat64: {
876 if (CpuFeatures::IsSupported(NEON)) {
877 InstructionOperand address = g.TempRegister();
878 {
879 // First we have to calculate the actual address.
880 InstructionCode add_opcode = kArmAdd;
881 InstructionOperand inputs[3];
882 inputs[0] = g.UseRegister(base);
883
884 size_t input_count;
885 if (TryMatchImmediateOrShift(this, &add_opcode, index, &input_count,
886 &inputs[1])) {
887 // input_count has been set by TryMatchImmediateOrShift(), so
888 // increment it to account for the base register in inputs[0].
889 input_count++;
890 } else {
891 add_opcode |= AddressingModeField::encode(kMode_Operand2_R);
892 inputs[1] = g.UseRegister(index);
893 input_count = 2; // Base register and index.
894 }
895
896 Emit(add_opcode, 1, &address, input_count, inputs);
897 }
898
899 inputs[input_count++] = g.UseRegister(value);
900 inputs[input_count++] = address;
901 InstructionCode op = kArmVst1F64;
902 op |= AddressingModeField::encode(kMode_Operand2_R);
903 Emit(op, 0, nullptr, input_count, inputs);
904 } else {
905 // Store a 64-bit floating point value using two 32-bit integer stores.
906 // Computing the store address here would require three live temporary
907 // registers (fp<63:32>, fp<31:0>, address), so compute base + 4 after
908 // storing the least-significant half of the value.
909
910 // First, move the 64-bit FP value into two temporary integer registers.
911 InstructionOperand fp[] = {g.TempRegister(), g.TempRegister()};
912 inputs[input_count++] = g.UseRegister(value);
913 Emit(kArmVmovU32U32F64, arraysize(fp), fp, input_count, inputs);
914
915 // Store the least-significant half.
916 inputs[0] = fp[0]; // Low 32-bits of FP value.
917 inputs[input_count++] =
918 g.UseRegister(base); // First store base address.
919 EmitStore(this, kArmStr, input_count, inputs, index);
920
921 // Store the most-significant half.
922 InstructionOperand base4 = g.TempRegister();
923 Emit(kArmAdd | AddressingModeField::encode(kMode_Operand2_I), base4,
924 g.UseRegister(base), g.TempImmediate(4)); // Compute base + 4.
925 inputs[0] = fp[1]; // High 32-bits of FP value.
926 inputs[1] = base4; // Second store base + 4 address.
927 EmitStore(this, kArmStr, input_count, inputs, index);
928 }
929 return;
930 }
931 default:
932 // All other cases should support unaligned accesses.
933 UNREACHABLE();
934 }
935 }
936
937 namespace {
938
EmitBic(InstructionSelector * selector,Node * node,Node * left,Node * right)939 void EmitBic(InstructionSelector* selector, Node* node, Node* left,
940 Node* right) {
941 ArmOperandGenerator g(selector);
942 InstructionCode opcode = kArmBic;
943 InstructionOperand value_operand;
944 InstructionOperand shift_operand;
945 if (TryMatchShift(selector, &opcode, right, &value_operand, &shift_operand)) {
946 selector->Emit(opcode, g.DefineAsRegister(node), g.UseRegister(left),
947 value_operand, shift_operand);
948 return;
949 }
950 selector->Emit(opcode | AddressingModeField::encode(kMode_Operand2_R),
951 g.DefineAsRegister(node), g.UseRegister(left),
952 g.UseRegister(right));
953 }
954
EmitUbfx(InstructionSelector * selector,Node * node,Node * left,uint32_t lsb,uint32_t width)955 void EmitUbfx(InstructionSelector* selector, Node* node, Node* left,
956 uint32_t lsb, uint32_t width) {
957 DCHECK_LE(lsb, 31u);
958 DCHECK_LE(1u, width);
959 DCHECK_LE(width, 32u - lsb);
960 ArmOperandGenerator g(selector);
961 selector->Emit(kArmUbfx, g.DefineAsRegister(node), g.UseRegister(left),
962 g.TempImmediate(lsb), g.TempImmediate(width));
963 }
964
965 } // namespace
966
VisitWord32And(Node * node)967 void InstructionSelector::VisitWord32And(Node* node) {
968 ArmOperandGenerator g(this);
969 Int32BinopMatcher m(node);
970 if (m.left().IsWord32Xor() && CanCover(node, m.left().node())) {
971 Int32BinopMatcher mleft(m.left().node());
972 if (mleft.right().Is(-1)) {
973 EmitBic(this, node, m.right().node(), mleft.left().node());
974 return;
975 }
976 }
977 if (m.right().IsWord32Xor() && CanCover(node, m.right().node())) {
978 Int32BinopMatcher mright(m.right().node());
979 if (mright.right().Is(-1)) {
980 EmitBic(this, node, m.left().node(), mright.left().node());
981 return;
982 }
983 }
984 if (m.right().HasResolvedValue()) {
985 uint32_t const value = m.right().ResolvedValue();
986 uint32_t width = base::bits::CountPopulation(value);
987 uint32_t leading_zeros = base::bits::CountLeadingZeros32(value);
988
989 // Try to merge SHR operations on the left hand input into this AND.
990 if (m.left().IsWord32Shr()) {
991 Int32BinopMatcher mshr(m.left().node());
992 if (mshr.right().HasResolvedValue()) {
993 uint32_t const shift = mshr.right().ResolvedValue();
994
995 if (((shift == 8) || (shift == 16) || (shift == 24)) &&
996 (value == 0xFF)) {
997 // Merge SHR into AND by emitting a UXTB instruction with a
998 // bytewise rotation.
999 Emit(kArmUxtb, g.DefineAsRegister(m.node()),
1000 g.UseRegister(mshr.left().node()),
1001 g.TempImmediate(mshr.right().ResolvedValue()));
1002 return;
1003 } else if (((shift == 8) || (shift == 16)) && (value == 0xFFFF)) {
1004 // Merge SHR into AND by emitting a UXTH instruction with a
1005 // bytewise rotation.
1006 Emit(kArmUxth, g.DefineAsRegister(m.node()),
1007 g.UseRegister(mshr.left().node()),
1008 g.TempImmediate(mshr.right().ResolvedValue()));
1009 return;
1010 } else if (IsSupported(ARMv7) && (width != 0) &&
1011 ((leading_zeros + width) == 32)) {
1012 // Merge Shr into And by emitting a UBFX instruction.
1013 DCHECK_EQ(0u, base::bits::CountTrailingZeros32(value));
1014 if ((1 <= shift) && (shift <= 31)) {
1015 // UBFX cannot extract bits past the register size, however since
1016 // shifting the original value would have introduced some zeros we
1017 // can still use UBFX with a smaller mask and the remaining bits
1018 // will be zeros.
1019 EmitUbfx(this, node, mshr.left().node(), shift,
1020 std::min(width, 32 - shift));
1021 return;
1022 }
1023 }
1024 }
1025 } else if (value == 0xFFFF) {
1026 // Emit UXTH for this AND. We don't bother testing for UXTB, as it's no
1027 // better than AND 0xFF for this operation.
1028 Emit(kArmUxth, g.DefineAsRegister(m.node()),
1029 g.UseRegister(m.left().node()), g.TempImmediate(0));
1030 return;
1031 }
1032 if (g.CanBeImmediate(~value)) {
1033 // Emit BIC for this AND by inverting the immediate value first.
1034 Emit(kArmBic | AddressingModeField::encode(kMode_Operand2_I),
1035 g.DefineAsRegister(node), g.UseRegister(m.left().node()),
1036 g.TempImmediate(~value));
1037 return;
1038 }
1039 if (!g.CanBeImmediate(value) && IsSupported(ARMv7)) {
1040 // If value has 9 to 23 contiguous set bits, and has the lsb set, we can
1041 // replace this AND with UBFX. Other contiguous bit patterns have already
1042 // been handled by BIC or will be handled by AND.
1043 if ((width != 0) && ((leading_zeros + width) == 32) &&
1044 (9 <= leading_zeros) && (leading_zeros <= 23)) {
1045 DCHECK_EQ(0u, base::bits::CountTrailingZeros32(value));
1046 EmitUbfx(this, node, m.left().node(), 0, width);
1047 return;
1048 }
1049
1050 width = 32 - width;
1051 leading_zeros = base::bits::CountLeadingZeros32(~value);
1052 uint32_t lsb = base::bits::CountTrailingZeros32(~value);
1053 if ((leading_zeros + width + lsb) == 32) {
1054 // This AND can be replaced with BFC.
1055 Emit(kArmBfc, g.DefineSameAsFirst(node), g.UseRegister(m.left().node()),
1056 g.TempImmediate(lsb), g.TempImmediate(width));
1057 return;
1058 }
1059 }
1060 }
1061 VisitBinop(this, node, kArmAnd, kArmAnd);
1062 }
1063
VisitWord32Or(Node * node)1064 void InstructionSelector::VisitWord32Or(Node* node) {
1065 VisitBinop(this, node, kArmOrr, kArmOrr);
1066 }
1067
VisitWord32Xor(Node * node)1068 void InstructionSelector::VisitWord32Xor(Node* node) {
1069 ArmOperandGenerator g(this);
1070 Int32BinopMatcher m(node);
1071 if (m.right().Is(-1)) {
1072 InstructionCode opcode = kArmMvn;
1073 InstructionOperand value_operand;
1074 InstructionOperand shift_operand;
1075 if (TryMatchShift(this, &opcode, m.left().node(), &value_operand,
1076 &shift_operand)) {
1077 Emit(opcode, g.DefineAsRegister(node), value_operand, shift_operand);
1078 return;
1079 }
1080 Emit(opcode | AddressingModeField::encode(kMode_Operand2_R),
1081 g.DefineAsRegister(node), g.UseRegister(m.left().node()));
1082 return;
1083 }
1084 VisitBinop(this, node, kArmEor, kArmEor);
1085 }
1086
VisitStackPointerGreaterThan(Node * node,FlagsContinuation * cont)1087 void InstructionSelector::VisitStackPointerGreaterThan(
1088 Node* node, FlagsContinuation* cont) {
1089 StackCheckKind kind = StackCheckKindOf(node->op());
1090 InstructionCode opcode =
1091 kArchStackPointerGreaterThan | MiscField::encode(static_cast<int>(kind));
1092
1093 ArmOperandGenerator g(this);
1094
1095 // No outputs.
1096 InstructionOperand* const outputs = nullptr;
1097 const int output_count = 0;
1098
1099 // Applying an offset to this stack check requires a temp register. Offsets
1100 // are only applied to the first stack check. If applying an offset, we must
1101 // ensure the input and temp registers do not alias, thus kUniqueRegister.
1102 InstructionOperand temps[] = {g.TempRegister()};
1103 const int temp_count = (kind == StackCheckKind::kJSFunctionEntry) ? 1 : 0;
1104 const auto register_mode = (kind == StackCheckKind::kJSFunctionEntry)
1105 ? OperandGenerator::kUniqueRegister
1106 : OperandGenerator::kRegister;
1107
1108 Node* const value = node->InputAt(0);
1109 InstructionOperand inputs[] = {g.UseRegisterWithMode(value, register_mode)};
1110 static constexpr int input_count = arraysize(inputs);
1111
1112 EmitWithContinuation(opcode, output_count, outputs, input_count, inputs,
1113 temp_count, temps, cont);
1114 }
1115
1116 namespace {
1117
1118 template <typename TryMatchShift>
VisitShift(InstructionSelector * selector,Node * node,TryMatchShift try_match_shift,FlagsContinuation * cont)1119 void VisitShift(InstructionSelector* selector, Node* node,
1120 TryMatchShift try_match_shift, FlagsContinuation* cont) {
1121 ArmOperandGenerator g(selector);
1122 InstructionCode opcode = kArmMov;
1123 InstructionOperand inputs[2];
1124 size_t input_count = 2;
1125 InstructionOperand outputs[1];
1126 size_t output_count = 0;
1127
1128 CHECK(try_match_shift(selector, &opcode, node, &inputs[0], &inputs[1]));
1129
1130 outputs[output_count++] = g.DefineAsRegister(node);
1131
1132 DCHECK_NE(0u, input_count);
1133 DCHECK_NE(0u, output_count);
1134 DCHECK_GE(arraysize(inputs), input_count);
1135 DCHECK_GE(arraysize(outputs), output_count);
1136 DCHECK_NE(kMode_None, AddressingModeField::decode(opcode));
1137
1138 selector->EmitWithContinuation(opcode, output_count, outputs, input_count,
1139 inputs, cont);
1140 }
1141
1142 template <typename TryMatchShift>
VisitShift(InstructionSelector * selector,Node * node,TryMatchShift try_match_shift)1143 void VisitShift(InstructionSelector* selector, Node* node,
1144 TryMatchShift try_match_shift) {
1145 FlagsContinuation cont;
1146 VisitShift(selector, node, try_match_shift, &cont);
1147 }
1148
1149 } // namespace
1150
VisitWord32Shl(Node * node)1151 void InstructionSelector::VisitWord32Shl(Node* node) {
1152 VisitShift(this, node, TryMatchLSL);
1153 }
1154
VisitWord32Shr(Node * node)1155 void InstructionSelector::VisitWord32Shr(Node* node) {
1156 ArmOperandGenerator g(this);
1157 Int32BinopMatcher m(node);
1158 if (IsSupported(ARMv7) && m.left().IsWord32And() &&
1159 m.right().IsInRange(0, 31)) {
1160 uint32_t lsb = m.right().ResolvedValue();
1161 Int32BinopMatcher mleft(m.left().node());
1162 if (mleft.right().HasResolvedValue()) {
1163 uint32_t value =
1164 static_cast<uint32_t>(mleft.right().ResolvedValue() >> lsb) << lsb;
1165 uint32_t width = base::bits::CountPopulation(value);
1166 uint32_t msb = base::bits::CountLeadingZeros32(value);
1167 if ((width != 0) && (msb + width + lsb == 32)) {
1168 DCHECK_EQ(lsb, base::bits::CountTrailingZeros32(value));
1169 return EmitUbfx(this, node, mleft.left().node(), lsb, width);
1170 }
1171 }
1172 }
1173 VisitShift(this, node, TryMatchLSR);
1174 }
1175
VisitWord32Sar(Node * node)1176 void InstructionSelector::VisitWord32Sar(Node* node) {
1177 ArmOperandGenerator g(this);
1178 Int32BinopMatcher m(node);
1179 if (CanCover(m.node(), m.left().node()) && m.left().IsWord32Shl()) {
1180 Int32BinopMatcher mleft(m.left().node());
1181 if (m.right().HasResolvedValue() && mleft.right().HasResolvedValue()) {
1182 uint32_t sar = m.right().ResolvedValue();
1183 uint32_t shl = mleft.right().ResolvedValue();
1184 if ((sar == shl) && (sar == 16)) {
1185 Emit(kArmSxth, g.DefineAsRegister(node),
1186 g.UseRegister(mleft.left().node()), g.TempImmediate(0));
1187 return;
1188 } else if ((sar == shl) && (sar == 24)) {
1189 Emit(kArmSxtb, g.DefineAsRegister(node),
1190 g.UseRegister(mleft.left().node()), g.TempImmediate(0));
1191 return;
1192 } else if (IsSupported(ARMv7) && (sar >= shl)) {
1193 Emit(kArmSbfx, g.DefineAsRegister(node),
1194 g.UseRegister(mleft.left().node()), g.TempImmediate(sar - shl),
1195 g.TempImmediate(32 - sar));
1196 return;
1197 }
1198 }
1199 }
1200 VisitShift(this, node, TryMatchASR);
1201 }
1202
VisitInt32PairAdd(Node * node)1203 void InstructionSelector::VisitInt32PairAdd(Node* node) {
1204 ArmOperandGenerator g(this);
1205
1206 Node* projection1 = NodeProperties::FindProjection(node, 1);
1207 if (projection1) {
1208 // We use UseUniqueRegister here to avoid register sharing with the output
1209 // registers.
1210 InstructionOperand inputs[] = {
1211 g.UseRegister(node->InputAt(0)), g.UseUniqueRegister(node->InputAt(1)),
1212 g.UseRegister(node->InputAt(2)), g.UseUniqueRegister(node->InputAt(3))};
1213
1214 InstructionOperand outputs[] = {
1215 g.DefineAsRegister(node),
1216 g.DefineAsRegister(NodeProperties::FindProjection(node, 1))};
1217
1218 Emit(kArmAddPair, 2, outputs, 4, inputs);
1219 } else {
1220 // The high word of the result is not used, so we emit the standard 32 bit
1221 // instruction.
1222 Emit(kArmAdd | AddressingModeField::encode(kMode_Operand2_R),
1223 g.DefineSameAsFirst(node), g.UseRegister(node->InputAt(0)),
1224 g.UseRegister(node->InputAt(2)));
1225 }
1226 }
1227
VisitInt32PairSub(Node * node)1228 void InstructionSelector::VisitInt32PairSub(Node* node) {
1229 ArmOperandGenerator g(this);
1230
1231 Node* projection1 = NodeProperties::FindProjection(node, 1);
1232 if (projection1) {
1233 // We use UseUniqueRegister here to avoid register sharing with the output
1234 // register.
1235 InstructionOperand inputs[] = {
1236 g.UseRegister(node->InputAt(0)), g.UseUniqueRegister(node->InputAt(1)),
1237 g.UseRegister(node->InputAt(2)), g.UseUniqueRegister(node->InputAt(3))};
1238
1239 InstructionOperand outputs[] = {
1240 g.DefineAsRegister(node),
1241 g.DefineAsRegister(NodeProperties::FindProjection(node, 1))};
1242
1243 Emit(kArmSubPair, 2, outputs, 4, inputs);
1244 } else {
1245 // The high word of the result is not used, so we emit the standard 32 bit
1246 // instruction.
1247 Emit(kArmSub | AddressingModeField::encode(kMode_Operand2_R),
1248 g.DefineSameAsFirst(node), g.UseRegister(node->InputAt(0)),
1249 g.UseRegister(node->InputAt(2)));
1250 }
1251 }
1252
VisitInt32PairMul(Node * node)1253 void InstructionSelector::VisitInt32PairMul(Node* node) {
1254 ArmOperandGenerator g(this);
1255 Node* projection1 = NodeProperties::FindProjection(node, 1);
1256 if (projection1) {
1257 InstructionOperand inputs[] = {g.UseUniqueRegister(node->InputAt(0)),
1258 g.UseUniqueRegister(node->InputAt(1)),
1259 g.UseUniqueRegister(node->InputAt(2)),
1260 g.UseUniqueRegister(node->InputAt(3))};
1261
1262 InstructionOperand outputs[] = {
1263 g.DefineAsRegister(node),
1264 g.DefineAsRegister(NodeProperties::FindProjection(node, 1))};
1265
1266 Emit(kArmMulPair, 2, outputs, 4, inputs);
1267 } else {
1268 // The high word of the result is not used, so we emit the standard 32 bit
1269 // instruction.
1270 Emit(kArmMul | AddressingModeField::encode(kMode_Operand2_R),
1271 g.DefineSameAsFirst(node), g.UseRegister(node->InputAt(0)),
1272 g.UseRegister(node->InputAt(2)));
1273 }
1274 }
1275
1276 namespace {
1277 // Shared routine for multiple shift operations.
VisitWord32PairShift(InstructionSelector * selector,InstructionCode opcode,Node * node)1278 void VisitWord32PairShift(InstructionSelector* selector, InstructionCode opcode,
1279 Node* node) {
1280 ArmOperandGenerator g(selector);
1281 // We use g.UseUniqueRegister here to guarantee that there is
1282 // no register aliasing of input registers with output registers.
1283 Int32Matcher m(node->InputAt(2));
1284 InstructionOperand shift_operand;
1285 if (m.HasResolvedValue()) {
1286 shift_operand = g.UseImmediate(m.node());
1287 } else {
1288 shift_operand = g.UseUniqueRegister(m.node());
1289 }
1290
1291 InstructionOperand inputs[] = {g.UseUniqueRegister(node->InputAt(0)),
1292 g.UseUniqueRegister(node->InputAt(1)),
1293 shift_operand};
1294
1295 Node* projection1 = NodeProperties::FindProjection(node, 1);
1296
1297 InstructionOperand outputs[2];
1298 InstructionOperand temps[1];
1299 int32_t output_count = 0;
1300 int32_t temp_count = 0;
1301
1302 outputs[output_count++] = g.DefineAsRegister(node);
1303 if (projection1) {
1304 outputs[output_count++] = g.DefineAsRegister(projection1);
1305 } else {
1306 temps[temp_count++] = g.TempRegister();
1307 }
1308
1309 selector->Emit(opcode, output_count, outputs, 3, inputs, temp_count, temps);
1310 }
1311 } // namespace
VisitWord32PairShl(Node * node)1312 void InstructionSelector::VisitWord32PairShl(Node* node) {
1313 VisitWord32PairShift(this, kArmLslPair, node);
1314 }
1315
VisitWord32PairShr(Node * node)1316 void InstructionSelector::VisitWord32PairShr(Node* node) {
1317 VisitWord32PairShift(this, kArmLsrPair, node);
1318 }
1319
VisitWord32PairSar(Node * node)1320 void InstructionSelector::VisitWord32PairSar(Node* node) {
1321 VisitWord32PairShift(this, kArmAsrPair, node);
1322 }
1323
VisitWord32Rol(Node * node)1324 void InstructionSelector::VisitWord32Rol(Node* node) { UNREACHABLE(); }
1325
VisitWord32Ror(Node * node)1326 void InstructionSelector::VisitWord32Ror(Node* node) {
1327 VisitShift(this, node, TryMatchROR);
1328 }
1329
VisitWord32Ctz(Node * node)1330 void InstructionSelector::VisitWord32Ctz(Node* node) { UNREACHABLE(); }
1331
VisitWord32ReverseBits(Node * node)1332 void InstructionSelector::VisitWord32ReverseBits(Node* node) {
1333 DCHECK(IsSupported(ARMv7));
1334 VisitRR(this, kArmRbit, node);
1335 }
1336
VisitWord64ReverseBytes(Node * node)1337 void InstructionSelector::VisitWord64ReverseBytes(Node* node) { UNREACHABLE(); }
1338
VisitWord32ReverseBytes(Node * node)1339 void InstructionSelector::VisitWord32ReverseBytes(Node* node) {
1340 VisitRR(this, kArmRev, node);
1341 }
1342
VisitSimd128ReverseBytes(Node * node)1343 void InstructionSelector::VisitSimd128ReverseBytes(Node* node) {
1344 UNREACHABLE();
1345 }
1346
VisitWord32Popcnt(Node * node)1347 void InstructionSelector::VisitWord32Popcnt(Node* node) { UNREACHABLE(); }
1348
VisitInt32Add(Node * node)1349 void InstructionSelector::VisitInt32Add(Node* node) {
1350 ArmOperandGenerator g(this);
1351 Int32BinopMatcher m(node);
1352 if (CanCover(node, m.left().node())) {
1353 switch (m.left().opcode()) {
1354 case IrOpcode::kInt32Mul: {
1355 Int32BinopMatcher mleft(m.left().node());
1356 Emit(kArmMla, g.DefineAsRegister(node),
1357 g.UseRegister(mleft.left().node()),
1358 g.UseRegister(mleft.right().node()),
1359 g.UseRegister(m.right().node()));
1360 return;
1361 }
1362 case IrOpcode::kInt32MulHigh: {
1363 Int32BinopMatcher mleft(m.left().node());
1364 Emit(kArmSmmla, g.DefineAsRegister(node),
1365 g.UseRegister(mleft.left().node()),
1366 g.UseRegister(mleft.right().node()),
1367 g.UseRegister(m.right().node()));
1368 return;
1369 }
1370 case IrOpcode::kWord32And: {
1371 Int32BinopMatcher mleft(m.left().node());
1372 if (mleft.right().Is(0xFF)) {
1373 Emit(kArmUxtab, g.DefineAsRegister(node),
1374 g.UseRegister(m.right().node()),
1375 g.UseRegister(mleft.left().node()), g.TempImmediate(0));
1376 return;
1377 } else if (mleft.right().Is(0xFFFF)) {
1378 Emit(kArmUxtah, g.DefineAsRegister(node),
1379 g.UseRegister(m.right().node()),
1380 g.UseRegister(mleft.left().node()), g.TempImmediate(0));
1381 return;
1382 }
1383 break;
1384 }
1385 case IrOpcode::kWord32Sar: {
1386 Int32BinopMatcher mleft(m.left().node());
1387 if (CanCover(mleft.node(), mleft.left().node()) &&
1388 mleft.left().IsWord32Shl()) {
1389 Int32BinopMatcher mleftleft(mleft.left().node());
1390 if (mleft.right().Is(24) && mleftleft.right().Is(24)) {
1391 Emit(kArmSxtab, g.DefineAsRegister(node),
1392 g.UseRegister(m.right().node()),
1393 g.UseRegister(mleftleft.left().node()), g.TempImmediate(0));
1394 return;
1395 } else if (mleft.right().Is(16) && mleftleft.right().Is(16)) {
1396 Emit(kArmSxtah, g.DefineAsRegister(node),
1397 g.UseRegister(m.right().node()),
1398 g.UseRegister(mleftleft.left().node()), g.TempImmediate(0));
1399 return;
1400 }
1401 }
1402 break;
1403 }
1404 default:
1405 break;
1406 }
1407 }
1408 if (CanCover(node, m.right().node())) {
1409 switch (m.right().opcode()) {
1410 case IrOpcode::kInt32Mul: {
1411 Int32BinopMatcher mright(m.right().node());
1412 Emit(kArmMla, g.DefineAsRegister(node),
1413 g.UseRegister(mright.left().node()),
1414 g.UseRegister(mright.right().node()),
1415 g.UseRegister(m.left().node()));
1416 return;
1417 }
1418 case IrOpcode::kInt32MulHigh: {
1419 Int32BinopMatcher mright(m.right().node());
1420 Emit(kArmSmmla, g.DefineAsRegister(node),
1421 g.UseRegister(mright.left().node()),
1422 g.UseRegister(mright.right().node()),
1423 g.UseRegister(m.left().node()));
1424 return;
1425 }
1426 case IrOpcode::kWord32And: {
1427 Int32BinopMatcher mright(m.right().node());
1428 if (mright.right().Is(0xFF)) {
1429 Emit(kArmUxtab, g.DefineAsRegister(node),
1430 g.UseRegister(m.left().node()),
1431 g.UseRegister(mright.left().node()), g.TempImmediate(0));
1432 return;
1433 } else if (mright.right().Is(0xFFFF)) {
1434 Emit(kArmUxtah, g.DefineAsRegister(node),
1435 g.UseRegister(m.left().node()),
1436 g.UseRegister(mright.left().node()), g.TempImmediate(0));
1437 return;
1438 }
1439 break;
1440 }
1441 case IrOpcode::kWord32Sar: {
1442 Int32BinopMatcher mright(m.right().node());
1443 if (CanCover(mright.node(), mright.left().node()) &&
1444 mright.left().IsWord32Shl()) {
1445 Int32BinopMatcher mrightleft(mright.left().node());
1446 if (mright.right().Is(24) && mrightleft.right().Is(24)) {
1447 Emit(kArmSxtab, g.DefineAsRegister(node),
1448 g.UseRegister(m.left().node()),
1449 g.UseRegister(mrightleft.left().node()), g.TempImmediate(0));
1450 return;
1451 } else if (mright.right().Is(16) && mrightleft.right().Is(16)) {
1452 Emit(kArmSxtah, g.DefineAsRegister(node),
1453 g.UseRegister(m.left().node()),
1454 g.UseRegister(mrightleft.left().node()), g.TempImmediate(0));
1455 return;
1456 }
1457 }
1458 break;
1459 }
1460 default:
1461 break;
1462 }
1463 }
1464 VisitBinop(this, node, kArmAdd, kArmAdd);
1465 }
1466
VisitInt32Sub(Node * node)1467 void InstructionSelector::VisitInt32Sub(Node* node) {
1468 ArmOperandGenerator g(this);
1469 Int32BinopMatcher m(node);
1470 if (IsSupported(ARMv7) && m.right().IsInt32Mul() &&
1471 CanCover(node, m.right().node())) {
1472 Int32BinopMatcher mright(m.right().node());
1473 Emit(kArmMls, g.DefineAsRegister(node), g.UseRegister(mright.left().node()),
1474 g.UseRegister(mright.right().node()), g.UseRegister(m.left().node()));
1475 return;
1476 }
1477 VisitBinop(this, node, kArmSub, kArmRsb);
1478 }
1479
1480 namespace {
1481
EmitInt32MulWithOverflow(InstructionSelector * selector,Node * node,FlagsContinuation * cont)1482 void EmitInt32MulWithOverflow(InstructionSelector* selector, Node* node,
1483 FlagsContinuation* cont) {
1484 ArmOperandGenerator g(selector);
1485 Int32BinopMatcher m(node);
1486 InstructionOperand result_operand = g.DefineAsRegister(node);
1487 InstructionOperand temp_operand = g.TempRegister();
1488 InstructionOperand outputs[] = {result_operand, temp_operand};
1489 InstructionOperand inputs[] = {g.UseRegister(m.left().node()),
1490 g.UseRegister(m.right().node())};
1491 selector->Emit(kArmSmull, 2, outputs, 2, inputs);
1492
1493 // result operand needs shift operator.
1494 InstructionOperand shift_31 = g.UseImmediate(31);
1495 InstructionCode opcode =
1496 kArmCmp | AddressingModeField::encode(kMode_Operand2_R_ASR_I);
1497 selector->EmitWithContinuation(opcode, temp_operand, result_operand, shift_31,
1498 cont);
1499 }
1500
1501 } // namespace
1502
VisitInt32Mul(Node * node)1503 void InstructionSelector::VisitInt32Mul(Node* node) {
1504 ArmOperandGenerator g(this);
1505 Int32BinopMatcher m(node);
1506 if (m.right().HasResolvedValue() && m.right().ResolvedValue() > 0) {
1507 int32_t value = m.right().ResolvedValue();
1508 if (base::bits::IsPowerOfTwo(value - 1)) {
1509 Emit(kArmAdd | AddressingModeField::encode(kMode_Operand2_R_LSL_I),
1510 g.DefineAsRegister(node), g.UseRegister(m.left().node()),
1511 g.UseRegister(m.left().node()),
1512 g.TempImmediate(base::bits::WhichPowerOfTwo(value - 1)));
1513 return;
1514 }
1515 if (value < kMaxInt && base::bits::IsPowerOfTwo(value + 1)) {
1516 Emit(kArmRsb | AddressingModeField::encode(kMode_Operand2_R_LSL_I),
1517 g.DefineAsRegister(node), g.UseRegister(m.left().node()),
1518 g.UseRegister(m.left().node()),
1519 g.TempImmediate(base::bits::WhichPowerOfTwo(value + 1)));
1520 return;
1521 }
1522 }
1523 VisitRRR(this, kArmMul, node);
1524 }
1525
VisitUint32MulHigh(Node * node)1526 void InstructionSelector::VisitUint32MulHigh(Node* node) {
1527 ArmOperandGenerator g(this);
1528 InstructionOperand outputs[] = {g.TempRegister(), g.DefineAsRegister(node)};
1529 InstructionOperand inputs[] = {g.UseRegister(node->InputAt(0)),
1530 g.UseRegister(node->InputAt(1))};
1531 Emit(kArmUmull, arraysize(outputs), outputs, arraysize(inputs), inputs);
1532 }
1533
VisitInt32Div(Node * node)1534 void InstructionSelector::VisitInt32Div(Node* node) {
1535 VisitDiv(this, node, kArmSdiv, kArmVcvtF64S32, kArmVcvtS32F64);
1536 }
1537
VisitUint32Div(Node * node)1538 void InstructionSelector::VisitUint32Div(Node* node) {
1539 VisitDiv(this, node, kArmUdiv, kArmVcvtF64U32, kArmVcvtU32F64);
1540 }
1541
VisitInt32Mod(Node * node)1542 void InstructionSelector::VisitInt32Mod(Node* node) {
1543 VisitMod(this, node, kArmSdiv, kArmVcvtF64S32, kArmVcvtS32F64);
1544 }
1545
VisitUint32Mod(Node * node)1546 void InstructionSelector::VisitUint32Mod(Node* node) {
1547 VisitMod(this, node, kArmUdiv, kArmVcvtF64U32, kArmVcvtU32F64);
1548 }
1549
1550 #define RR_OP_LIST(V) \
1551 V(Word32Clz, kArmClz) \
1552 V(ChangeFloat32ToFloat64, kArmVcvtF64F32) \
1553 V(RoundInt32ToFloat32, kArmVcvtF32S32) \
1554 V(RoundUint32ToFloat32, kArmVcvtF32U32) \
1555 V(ChangeInt32ToFloat64, kArmVcvtF64S32) \
1556 V(ChangeUint32ToFloat64, kArmVcvtF64U32) \
1557 V(ChangeFloat64ToInt32, kArmVcvtS32F64) \
1558 V(ChangeFloat64ToUint32, kArmVcvtU32F64) \
1559 V(TruncateFloat64ToUint32, kArmVcvtU32F64) \
1560 V(TruncateFloat64ToFloat32, kArmVcvtF32F64) \
1561 V(TruncateFloat64ToWord32, kArchTruncateDoubleToI) \
1562 V(RoundFloat64ToInt32, kArmVcvtS32F64) \
1563 V(BitcastFloat32ToInt32, kArmVmovU32F32) \
1564 V(BitcastInt32ToFloat32, kArmVmovF32U32) \
1565 V(Float64ExtractLowWord32, kArmVmovLowU32F64) \
1566 V(Float64ExtractHighWord32, kArmVmovHighU32F64) \
1567 V(Float64SilenceNaN, kArmFloat64SilenceNaN) \
1568 V(Float32Abs, kArmVabsF32) \
1569 V(Float64Abs, kArmVabsF64) \
1570 V(Float32Neg, kArmVnegF32) \
1571 V(Float64Neg, kArmVnegF64) \
1572 V(Float32Sqrt, kArmVsqrtF32) \
1573 V(Float64Sqrt, kArmVsqrtF64)
1574
1575 #define RR_OP_LIST_V8(V) \
1576 V(Float32RoundDown, kArmVrintmF32) \
1577 V(Float64RoundDown, kArmVrintmF64) \
1578 V(Float32RoundUp, kArmVrintpF32) \
1579 V(Float64RoundUp, kArmVrintpF64) \
1580 V(Float32RoundTruncate, kArmVrintzF32) \
1581 V(Float64RoundTruncate, kArmVrintzF64) \
1582 V(Float64RoundTiesAway, kArmVrintaF64) \
1583 V(Float32RoundTiesEven, kArmVrintnF32) \
1584 V(Float64RoundTiesEven, kArmVrintnF64) \
1585 V(F64x2Ceil, kArmF64x2Ceil) \
1586 V(F64x2Floor, kArmF64x2Floor) \
1587 V(F64x2Trunc, kArmF64x2Trunc) \
1588 V(F64x2NearestInt, kArmF64x2NearestInt) \
1589 V(F32x4Ceil, kArmVrintpF32) \
1590 V(F32x4Floor, kArmVrintmF32) \
1591 V(F32x4Trunc, kArmVrintzF32) \
1592 V(F32x4NearestInt, kArmVrintnF32)
1593
1594 #define RRR_OP_LIST(V) \
1595 V(Int32MulHigh, kArmSmmul) \
1596 V(Float32Mul, kArmVmulF32) \
1597 V(Float64Mul, kArmVmulF64) \
1598 V(Float32Div, kArmVdivF32) \
1599 V(Float64Div, kArmVdivF64) \
1600 V(Float32Max, kArmFloat32Max) \
1601 V(Float64Max, kArmFloat64Max) \
1602 V(Float32Min, kArmFloat32Min) \
1603 V(Float64Min, kArmFloat64Min)
1604
1605 #define RR_VISITOR(Name, opcode) \
1606 void InstructionSelector::Visit##Name(Node* node) { \
1607 VisitRR(this, opcode, node); \
1608 }
1609 RR_OP_LIST(RR_VISITOR)
1610 #undef RR_VISITOR
1611 #undef RR_OP_LIST
1612
1613 #define RR_VISITOR_V8(Name, opcode) \
1614 void InstructionSelector::Visit##Name(Node* node) { \
1615 DCHECK(CpuFeatures::IsSupported(ARMv8)); \
1616 VisitRR(this, opcode, node); \
1617 }
RR_OP_LIST_V8(RR_VISITOR_V8)1618 RR_OP_LIST_V8(RR_VISITOR_V8)
1619 #undef RR_VISITOR_V8
1620 #undef RR_OP_LIST_V8
1621
1622 #define RRR_VISITOR(Name, opcode) \
1623 void InstructionSelector::Visit##Name(Node* node) { \
1624 VisitRRR(this, opcode, node); \
1625 }
1626 RRR_OP_LIST(RRR_VISITOR)
1627 #undef RRR_VISITOR
1628 #undef RRR_OP_LIST
1629
1630 void InstructionSelector::VisitFloat32Add(Node* node) {
1631 ArmOperandGenerator g(this);
1632 Float32BinopMatcher m(node);
1633 if (m.left().IsFloat32Mul() && CanCover(node, m.left().node())) {
1634 Float32BinopMatcher mleft(m.left().node());
1635 Emit(kArmVmlaF32, g.DefineSameAsFirst(node),
1636 g.UseRegister(m.right().node()), g.UseRegister(mleft.left().node()),
1637 g.UseRegister(mleft.right().node()));
1638 return;
1639 }
1640 if (m.right().IsFloat32Mul() && CanCover(node, m.right().node())) {
1641 Float32BinopMatcher mright(m.right().node());
1642 Emit(kArmVmlaF32, g.DefineSameAsFirst(node), g.UseRegister(m.left().node()),
1643 g.UseRegister(mright.left().node()),
1644 g.UseRegister(mright.right().node()));
1645 return;
1646 }
1647 VisitRRR(this, kArmVaddF32, node);
1648 }
1649
VisitFloat64Add(Node * node)1650 void InstructionSelector::VisitFloat64Add(Node* node) {
1651 ArmOperandGenerator g(this);
1652 Float64BinopMatcher m(node);
1653 if (m.left().IsFloat64Mul() && CanCover(node, m.left().node())) {
1654 Float64BinopMatcher mleft(m.left().node());
1655 Emit(kArmVmlaF64, g.DefineSameAsFirst(node),
1656 g.UseRegister(m.right().node()), g.UseRegister(mleft.left().node()),
1657 g.UseRegister(mleft.right().node()));
1658 return;
1659 }
1660 if (m.right().IsFloat64Mul() && CanCover(node, m.right().node())) {
1661 Float64BinopMatcher mright(m.right().node());
1662 Emit(kArmVmlaF64, g.DefineSameAsFirst(node), g.UseRegister(m.left().node()),
1663 g.UseRegister(mright.left().node()),
1664 g.UseRegister(mright.right().node()));
1665 return;
1666 }
1667 VisitRRR(this, kArmVaddF64, node);
1668 }
1669
VisitFloat32Sub(Node * node)1670 void InstructionSelector::VisitFloat32Sub(Node* node) {
1671 ArmOperandGenerator g(this);
1672 Float32BinopMatcher m(node);
1673 if (m.right().IsFloat32Mul() && CanCover(node, m.right().node())) {
1674 Float32BinopMatcher mright(m.right().node());
1675 Emit(kArmVmlsF32, g.DefineSameAsFirst(node), g.UseRegister(m.left().node()),
1676 g.UseRegister(mright.left().node()),
1677 g.UseRegister(mright.right().node()));
1678 return;
1679 }
1680 VisitRRR(this, kArmVsubF32, node);
1681 }
1682
VisitFloat64Sub(Node * node)1683 void InstructionSelector::VisitFloat64Sub(Node* node) {
1684 ArmOperandGenerator g(this);
1685 Float64BinopMatcher m(node);
1686 if (m.right().IsFloat64Mul() && CanCover(node, m.right().node())) {
1687 Float64BinopMatcher mright(m.right().node());
1688 Emit(kArmVmlsF64, g.DefineSameAsFirst(node), g.UseRegister(m.left().node()),
1689 g.UseRegister(mright.left().node()),
1690 g.UseRegister(mright.right().node()));
1691 return;
1692 }
1693 VisitRRR(this, kArmVsubF64, node);
1694 }
1695
VisitFloat64Mod(Node * node)1696 void InstructionSelector::VisitFloat64Mod(Node* node) {
1697 ArmOperandGenerator g(this);
1698 Emit(kArmVmodF64, g.DefineAsFixed(node, d0), g.UseFixed(node->InputAt(0), d0),
1699 g.UseFixed(node->InputAt(1), d1))
1700 ->MarkAsCall();
1701 }
1702
VisitFloat64Ieee754Binop(Node * node,InstructionCode opcode)1703 void InstructionSelector::VisitFloat64Ieee754Binop(Node* node,
1704 InstructionCode opcode) {
1705 ArmOperandGenerator g(this);
1706 Emit(opcode, g.DefineAsFixed(node, d0), g.UseFixed(node->InputAt(0), d0),
1707 g.UseFixed(node->InputAt(1), d1))
1708 ->MarkAsCall();
1709 }
1710
VisitFloat64Ieee754Unop(Node * node,InstructionCode opcode)1711 void InstructionSelector::VisitFloat64Ieee754Unop(Node* node,
1712 InstructionCode opcode) {
1713 ArmOperandGenerator g(this);
1714 Emit(opcode, g.DefineAsFixed(node, d0), g.UseFixed(node->InputAt(0), d0))
1715 ->MarkAsCall();
1716 }
1717
EmitPrepareArguments(ZoneVector<PushParameter> * arguments,const CallDescriptor * call_descriptor,Node * node)1718 void InstructionSelector::EmitPrepareArguments(
1719 ZoneVector<PushParameter>* arguments, const CallDescriptor* call_descriptor,
1720 Node* node) {
1721 ArmOperandGenerator g(this);
1722
1723 // Prepare for C function call.
1724 if (call_descriptor->IsCFunctionCall()) {
1725 Emit(kArchPrepareCallCFunction | MiscField::encode(static_cast<int>(
1726 call_descriptor->ParameterCount())),
1727 0, nullptr, 0, nullptr);
1728
1729 // Poke any stack arguments.
1730 for (size_t n = 0; n < arguments->size(); ++n) {
1731 PushParameter input = (*arguments)[n];
1732 if (input.node) {
1733 int slot = static_cast<int>(n);
1734 Emit(kArmPoke | MiscField::encode(slot), g.NoOutput(),
1735 g.UseRegister(input.node));
1736 }
1737 }
1738 } else {
1739 // Push any stack arguments.
1740 int stack_decrement = 0;
1741 for (PushParameter input : base::Reversed(*arguments)) {
1742 stack_decrement += kSystemPointerSize;
1743 // Skip any alignment holes in pushed nodes.
1744 if (input.node == nullptr) continue;
1745 InstructionOperand decrement = g.UseImmediate(stack_decrement);
1746 stack_decrement = 0;
1747 Emit(kArmPush, g.NoOutput(), decrement, g.UseRegister(input.node));
1748 }
1749 }
1750 }
1751
EmitPrepareResults(ZoneVector<PushParameter> * results,const CallDescriptor * call_descriptor,Node * node)1752 void InstructionSelector::EmitPrepareResults(
1753 ZoneVector<PushParameter>* results, const CallDescriptor* call_descriptor,
1754 Node* node) {
1755 ArmOperandGenerator g(this);
1756
1757 for (PushParameter output : *results) {
1758 if (!output.location.IsCallerFrameSlot()) continue;
1759 // Skip any alignment holes in nodes.
1760 if (output.node != nullptr) {
1761 DCHECK(!call_descriptor->IsCFunctionCall());
1762 if (output.location.GetType() == MachineType::Float32()) {
1763 MarkAsFloat32(output.node);
1764 } else if (output.location.GetType() == MachineType::Float64()) {
1765 MarkAsFloat64(output.node);
1766 } else if (output.location.GetType() == MachineType::Simd128()) {
1767 MarkAsSimd128(output.node);
1768 }
1769 int offset = call_descriptor->GetOffsetToReturns();
1770 int reverse_slot = -output.location.GetLocation() - offset;
1771 Emit(kArmPeek, g.DefineAsRegister(output.node),
1772 g.UseImmediate(reverse_slot));
1773 }
1774 }
1775 }
1776
IsTailCallAddressImmediate()1777 bool InstructionSelector::IsTailCallAddressImmediate() { return false; }
1778
1779 namespace {
1780
1781 // Shared routine for multiple compare operations.
VisitCompare(InstructionSelector * selector,InstructionCode opcode,InstructionOperand left,InstructionOperand right,FlagsContinuation * cont)1782 void VisitCompare(InstructionSelector* selector, InstructionCode opcode,
1783 InstructionOperand left, InstructionOperand right,
1784 FlagsContinuation* cont) {
1785 selector->EmitWithContinuation(opcode, left, right, cont);
1786 }
1787
1788 // Shared routine for multiple float32 compare operations.
VisitFloat32Compare(InstructionSelector * selector,Node * node,FlagsContinuation * cont)1789 void VisitFloat32Compare(InstructionSelector* selector, Node* node,
1790 FlagsContinuation* cont) {
1791 ArmOperandGenerator g(selector);
1792 Float32BinopMatcher m(node);
1793 if (m.right().Is(0.0f)) {
1794 VisitCompare(selector, kArmVcmpF32, g.UseRegister(m.left().node()),
1795 g.UseImmediate(m.right().node()), cont);
1796 } else if (m.left().Is(0.0f)) {
1797 cont->Commute();
1798 VisitCompare(selector, kArmVcmpF32, g.UseRegister(m.right().node()),
1799 g.UseImmediate(m.left().node()), cont);
1800 } else {
1801 VisitCompare(selector, kArmVcmpF32, g.UseRegister(m.left().node()),
1802 g.UseRegister(m.right().node()), cont);
1803 }
1804 }
1805
1806 // Shared routine for multiple float64 compare operations.
VisitFloat64Compare(InstructionSelector * selector,Node * node,FlagsContinuation * cont)1807 void VisitFloat64Compare(InstructionSelector* selector, Node* node,
1808 FlagsContinuation* cont) {
1809 ArmOperandGenerator g(selector);
1810 Float64BinopMatcher m(node);
1811 if (m.right().Is(0.0)) {
1812 VisitCompare(selector, kArmVcmpF64, g.UseRegister(m.left().node()),
1813 g.UseImmediate(m.right().node()), cont);
1814 } else if (m.left().Is(0.0)) {
1815 cont->Commute();
1816 VisitCompare(selector, kArmVcmpF64, g.UseRegister(m.right().node()),
1817 g.UseImmediate(m.left().node()), cont);
1818 } else {
1819 VisitCompare(selector, kArmVcmpF64, g.UseRegister(m.left().node()),
1820 g.UseRegister(m.right().node()), cont);
1821 }
1822 }
1823
1824 // Check whether we can convert:
1825 // ((a <op> b) cmp 0), b.<cond>
1826 // to:
1827 // (a <ops> b), b.<cond'>
1828 // where <ops> is the flag setting version of <op>.
1829 // We only generate conditions <cond'> that are a combination of the N
1830 // and Z flags. This avoids the need to make this function dependent on
1831 // the flag-setting operation.
CanUseFlagSettingBinop(FlagsCondition cond)1832 bool CanUseFlagSettingBinop(FlagsCondition cond) {
1833 switch (cond) {
1834 case kEqual:
1835 case kNotEqual:
1836 case kSignedLessThan:
1837 case kSignedGreaterThanOrEqual:
1838 case kUnsignedLessThanOrEqual: // x <= 0 -> x == 0
1839 case kUnsignedGreaterThan: // x > 0 -> x != 0
1840 return true;
1841 default:
1842 return false;
1843 }
1844 }
1845
1846 // Map <cond> to <cond'> so that the following transformation is possible:
1847 // ((a <op> b) cmp 0), b.<cond>
1848 // to:
1849 // (a <ops> b), b.<cond'>
1850 // where <ops> is the flag setting version of <op>.
MapForFlagSettingBinop(FlagsCondition cond)1851 FlagsCondition MapForFlagSettingBinop(FlagsCondition cond) {
1852 DCHECK(CanUseFlagSettingBinop(cond));
1853 switch (cond) {
1854 case kEqual:
1855 case kNotEqual:
1856 return cond;
1857 case kSignedLessThan:
1858 return kNegative;
1859 case kSignedGreaterThanOrEqual:
1860 return kPositiveOrZero;
1861 case kUnsignedLessThanOrEqual: // x <= 0 -> x == 0
1862 return kEqual;
1863 case kUnsignedGreaterThan: // x > 0 -> x != 0
1864 return kNotEqual;
1865 default:
1866 UNREACHABLE();
1867 }
1868 }
1869
1870 // Check if we can perform the transformation:
1871 // ((a <op> b) cmp 0), b.<cond>
1872 // to:
1873 // (a <ops> b), b.<cond'>
1874 // where <ops> is the flag setting version of <op>, and if so,
1875 // updates {node}, {opcode} and {cont} accordingly.
MaybeReplaceCmpZeroWithFlagSettingBinop(InstructionSelector * selector,Node ** node,Node * binop,InstructionCode * opcode,FlagsCondition cond,FlagsContinuation * cont)1876 void MaybeReplaceCmpZeroWithFlagSettingBinop(InstructionSelector* selector,
1877 Node** node, Node* binop,
1878 InstructionCode* opcode,
1879 FlagsCondition cond,
1880 FlagsContinuation* cont) {
1881 InstructionCode binop_opcode;
1882 InstructionCode no_output_opcode;
1883 switch (binop->opcode()) {
1884 case IrOpcode::kInt32Add:
1885 binop_opcode = kArmAdd;
1886 no_output_opcode = kArmCmn;
1887 break;
1888 case IrOpcode::kWord32And:
1889 binop_opcode = kArmAnd;
1890 no_output_opcode = kArmTst;
1891 break;
1892 case IrOpcode::kWord32Or:
1893 binop_opcode = kArmOrr;
1894 no_output_opcode = kArmOrr;
1895 break;
1896 case IrOpcode::kWord32Xor:
1897 binop_opcode = kArmEor;
1898 no_output_opcode = kArmTeq;
1899 break;
1900 default:
1901 UNREACHABLE();
1902 }
1903 if (selector->CanCover(*node, binop)) {
1904 // The comparison is the only user of {node}.
1905 cont->Overwrite(MapForFlagSettingBinop(cond));
1906 *opcode = no_output_opcode;
1907 *node = binop;
1908 } else if (selector->IsOnlyUserOfNodeInSameBlock(*node, binop)) {
1909 // We can also handle the case where the {node} and the comparison are in
1910 // the same basic block, and the comparison is the only user of {node} in
1911 // this basic block ({node} has users in other basic blocks).
1912 cont->Overwrite(MapForFlagSettingBinop(cond));
1913 *opcode = binop_opcode;
1914 *node = binop;
1915 }
1916 }
1917
1918 // Shared routine for multiple word compare operations.
VisitWordCompare(InstructionSelector * selector,Node * node,InstructionCode opcode,FlagsContinuation * cont)1919 void VisitWordCompare(InstructionSelector* selector, Node* node,
1920 InstructionCode opcode, FlagsContinuation* cont) {
1921 ArmOperandGenerator g(selector);
1922 Int32BinopMatcher m(node);
1923 InstructionOperand inputs[3];
1924 size_t input_count = 0;
1925 InstructionOperand outputs[2];
1926 size_t output_count = 0;
1927 bool has_result = (opcode != kArmCmp) && (opcode != kArmCmn) &&
1928 (opcode != kArmTst) && (opcode != kArmTeq);
1929
1930 if (TryMatchImmediateOrShift(selector, &opcode, m.right().node(),
1931 &input_count, &inputs[1])) {
1932 inputs[0] = g.UseRegister(m.left().node());
1933 input_count++;
1934 } else if (TryMatchImmediateOrShift(selector, &opcode, m.left().node(),
1935 &input_count, &inputs[1])) {
1936 if (!node->op()->HasProperty(Operator::kCommutative)) cont->Commute();
1937 inputs[0] = g.UseRegister(m.right().node());
1938 input_count++;
1939 } else {
1940 opcode |= AddressingModeField::encode(kMode_Operand2_R);
1941 inputs[input_count++] = g.UseRegister(m.left().node());
1942 inputs[input_count++] = g.UseRegister(m.right().node());
1943 }
1944
1945 if (has_result) {
1946 if (cont->IsDeoptimize()) {
1947 // If we can deoptimize as a result of the binop, we need to make sure
1948 // that the deopt inputs are not overwritten by the binop result. One way
1949 // to achieve that is to declare the output register as same-as-first.
1950 outputs[output_count++] = g.DefineSameAsFirst(node);
1951 } else {
1952 outputs[output_count++] = g.DefineAsRegister(node);
1953 }
1954 }
1955
1956 DCHECK_NE(0u, input_count);
1957 DCHECK_GE(arraysize(inputs), input_count);
1958 DCHECK_GE(arraysize(outputs), output_count);
1959
1960 selector->EmitWithContinuation(opcode, output_count, outputs, input_count,
1961 inputs, cont);
1962 }
1963
VisitWordCompare(InstructionSelector * selector,Node * node,FlagsContinuation * cont)1964 void VisitWordCompare(InstructionSelector* selector, Node* node,
1965 FlagsContinuation* cont) {
1966 InstructionCode opcode = kArmCmp;
1967 Int32BinopMatcher m(node);
1968
1969 FlagsCondition cond = cont->condition();
1970 if (m.right().Is(0) && (m.left().IsInt32Add() || m.left().IsWord32Or() ||
1971 m.left().IsWord32And() || m.left().IsWord32Xor())) {
1972 // Emit flag setting instructions for comparisons against zero.
1973 if (CanUseFlagSettingBinop(cond)) {
1974 Node* binop = m.left().node();
1975 MaybeReplaceCmpZeroWithFlagSettingBinop(selector, &node, binop, &opcode,
1976 cond, cont);
1977 }
1978 } else if (m.left().Is(0) &&
1979 (m.right().IsInt32Add() || m.right().IsWord32Or() ||
1980 m.right().IsWord32And() || m.right().IsWord32Xor())) {
1981 // Same as above, but we need to commute the condition before we
1982 // continue with the rest of the checks.
1983 cond = CommuteFlagsCondition(cond);
1984 if (CanUseFlagSettingBinop(cond)) {
1985 Node* binop = m.right().node();
1986 MaybeReplaceCmpZeroWithFlagSettingBinop(selector, &node, binop, &opcode,
1987 cond, cont);
1988 }
1989 }
1990
1991 VisitWordCompare(selector, node, opcode, cont);
1992 }
1993
1994 } // namespace
1995
1996 // Shared routine for word comparisons against zero.
VisitWordCompareZero(Node * user,Node * value,FlagsContinuation * cont)1997 void InstructionSelector::VisitWordCompareZero(Node* user, Node* value,
1998 FlagsContinuation* cont) {
1999 // Try to combine with comparisons against 0 by simply inverting the branch.
2000 while (value->opcode() == IrOpcode::kWord32Equal && CanCover(user, value)) {
2001 Int32BinopMatcher m(value);
2002 if (!m.right().Is(0)) break;
2003
2004 user = value;
2005 value = m.left().node();
2006 cont->Negate();
2007 }
2008
2009 if (CanCover(user, value)) {
2010 switch (value->opcode()) {
2011 case IrOpcode::kWord32Equal:
2012 cont->OverwriteAndNegateIfEqual(kEqual);
2013 return VisitWordCompare(this, value, cont);
2014 case IrOpcode::kInt32LessThan:
2015 cont->OverwriteAndNegateIfEqual(kSignedLessThan);
2016 return VisitWordCompare(this, value, cont);
2017 case IrOpcode::kInt32LessThanOrEqual:
2018 cont->OverwriteAndNegateIfEqual(kSignedLessThanOrEqual);
2019 return VisitWordCompare(this, value, cont);
2020 case IrOpcode::kUint32LessThan:
2021 cont->OverwriteAndNegateIfEqual(kUnsignedLessThan);
2022 return VisitWordCompare(this, value, cont);
2023 case IrOpcode::kUint32LessThanOrEqual:
2024 cont->OverwriteAndNegateIfEqual(kUnsignedLessThanOrEqual);
2025 return VisitWordCompare(this, value, cont);
2026 case IrOpcode::kFloat32Equal:
2027 cont->OverwriteAndNegateIfEqual(kEqual);
2028 return VisitFloat32Compare(this, value, cont);
2029 case IrOpcode::kFloat32LessThan:
2030 cont->OverwriteAndNegateIfEqual(kFloatLessThan);
2031 return VisitFloat32Compare(this, value, cont);
2032 case IrOpcode::kFloat32LessThanOrEqual:
2033 cont->OverwriteAndNegateIfEqual(kFloatLessThanOrEqual);
2034 return VisitFloat32Compare(this, value, cont);
2035 case IrOpcode::kFloat64Equal:
2036 cont->OverwriteAndNegateIfEqual(kEqual);
2037 return VisitFloat64Compare(this, value, cont);
2038 case IrOpcode::kFloat64LessThan:
2039 cont->OverwriteAndNegateIfEqual(kFloatLessThan);
2040 return VisitFloat64Compare(this, value, cont);
2041 case IrOpcode::kFloat64LessThanOrEqual:
2042 cont->OverwriteAndNegateIfEqual(kFloatLessThanOrEqual);
2043 return VisitFloat64Compare(this, value, cont);
2044 case IrOpcode::kProjection:
2045 // Check if this is the overflow output projection of an
2046 // <Operation>WithOverflow node.
2047 if (ProjectionIndexOf(value->op()) == 1u) {
2048 // We cannot combine the <Operation>WithOverflow with this branch
2049 // unless the 0th projection (the use of the actual value of the
2050 // <Operation> is either nullptr, which means there's no use of the
2051 // actual value, or was already defined, which means it is scheduled
2052 // *AFTER* this branch).
2053 Node* const node = value->InputAt(0);
2054 Node* const result = NodeProperties::FindProjection(node, 0);
2055 if (!result || IsDefined(result)) {
2056 switch (node->opcode()) {
2057 case IrOpcode::kInt32AddWithOverflow:
2058 cont->OverwriteAndNegateIfEqual(kOverflow);
2059 return VisitBinop(this, node, kArmAdd, kArmAdd, cont);
2060 case IrOpcode::kInt32SubWithOverflow:
2061 cont->OverwriteAndNegateIfEqual(kOverflow);
2062 return VisitBinop(this, node, kArmSub, kArmRsb, cont);
2063 case IrOpcode::kInt32MulWithOverflow:
2064 // ARM doesn't set the overflow flag for multiplication, so we
2065 // need to test on kNotEqual. Here is the code sequence used:
2066 // smull resultlow, resulthigh, left, right
2067 // cmp resulthigh, Operand(resultlow, ASR, 31)
2068 cont->OverwriteAndNegateIfEqual(kNotEqual);
2069 return EmitInt32MulWithOverflow(this, node, cont);
2070 default:
2071 break;
2072 }
2073 }
2074 }
2075 break;
2076 case IrOpcode::kInt32Add:
2077 return VisitWordCompare(this, value, kArmCmn, cont);
2078 case IrOpcode::kInt32Sub:
2079 return VisitWordCompare(this, value, kArmCmp, cont);
2080 case IrOpcode::kWord32And:
2081 return VisitWordCompare(this, value, kArmTst, cont);
2082 case IrOpcode::kWord32Or:
2083 return VisitBinop(this, value, kArmOrr, kArmOrr, cont);
2084 case IrOpcode::kWord32Xor:
2085 return VisitWordCompare(this, value, kArmTeq, cont);
2086 case IrOpcode::kWord32Sar:
2087 return VisitShift(this, value, TryMatchASR, cont);
2088 case IrOpcode::kWord32Shl:
2089 return VisitShift(this, value, TryMatchLSL, cont);
2090 case IrOpcode::kWord32Shr:
2091 return VisitShift(this, value, TryMatchLSR, cont);
2092 case IrOpcode::kWord32Ror:
2093 return VisitShift(this, value, TryMatchROR, cont);
2094 case IrOpcode::kStackPointerGreaterThan:
2095 cont->OverwriteAndNegateIfEqual(kStackPointerGreaterThanCondition);
2096 return VisitStackPointerGreaterThan(value, cont);
2097 default:
2098 break;
2099 }
2100 }
2101
2102 if (user->opcode() == IrOpcode::kWord32Equal) {
2103 return VisitWordCompare(this, user, cont);
2104 }
2105
2106 // Continuation could not be combined with a compare, emit compare against 0.
2107 ArmOperandGenerator g(this);
2108 InstructionCode const opcode =
2109 kArmTst | AddressingModeField::encode(kMode_Operand2_R);
2110 InstructionOperand const value_operand = g.UseRegister(value);
2111 EmitWithContinuation(opcode, value_operand, value_operand, cont);
2112 }
2113
VisitSwitch(Node * node,const SwitchInfo & sw)2114 void InstructionSelector::VisitSwitch(Node* node, const SwitchInfo& sw) {
2115 ArmOperandGenerator g(this);
2116 InstructionOperand value_operand = g.UseRegister(node->InputAt(0));
2117
2118 // Emit either ArchTableSwitch or ArchBinarySearchSwitch.
2119 if (enable_switch_jump_table_ == kEnableSwitchJumpTable) {
2120 static const size_t kMaxTableSwitchValueRange = 2 << 16;
2121 size_t table_space_cost = 4 + sw.value_range();
2122 size_t table_time_cost = 3;
2123 size_t lookup_space_cost = 3 + 2 * sw.case_count();
2124 size_t lookup_time_cost = sw.case_count();
2125 if (sw.case_count() > 0 &&
2126 table_space_cost + 3 * table_time_cost <=
2127 lookup_space_cost + 3 * lookup_time_cost &&
2128 sw.min_value() > std::numeric_limits<int32_t>::min() &&
2129 sw.value_range() <= kMaxTableSwitchValueRange) {
2130 InstructionOperand index_operand = value_operand;
2131 if (sw.min_value()) {
2132 index_operand = g.TempRegister();
2133 Emit(kArmSub | AddressingModeField::encode(kMode_Operand2_I),
2134 index_operand, value_operand, g.TempImmediate(sw.min_value()));
2135 }
2136 // Generate a table lookup.
2137 return EmitTableSwitch(sw, index_operand);
2138 }
2139 }
2140
2141 // Generate a tree of conditional jumps.
2142 return EmitBinarySearchSwitch(sw, value_operand);
2143 }
2144
VisitWord32Equal(Node * const node)2145 void InstructionSelector::VisitWord32Equal(Node* const node) {
2146 FlagsContinuation cont = FlagsContinuation::ForSet(kEqual, node);
2147 Int32BinopMatcher m(node);
2148 if (m.right().Is(0)) {
2149 return VisitWordCompareZero(m.node(), m.left().node(), &cont);
2150 }
2151 VisitWordCompare(this, node, &cont);
2152 }
2153
VisitInt32LessThan(Node * node)2154 void InstructionSelector::VisitInt32LessThan(Node* node) {
2155 FlagsContinuation cont = FlagsContinuation::ForSet(kSignedLessThan, node);
2156 VisitWordCompare(this, node, &cont);
2157 }
2158
VisitInt32LessThanOrEqual(Node * node)2159 void InstructionSelector::VisitInt32LessThanOrEqual(Node* node) {
2160 FlagsContinuation cont =
2161 FlagsContinuation::ForSet(kSignedLessThanOrEqual, node);
2162 VisitWordCompare(this, node, &cont);
2163 }
2164
VisitUint32LessThan(Node * node)2165 void InstructionSelector::VisitUint32LessThan(Node* node) {
2166 FlagsContinuation cont = FlagsContinuation::ForSet(kUnsignedLessThan, node);
2167 VisitWordCompare(this, node, &cont);
2168 }
2169
VisitUint32LessThanOrEqual(Node * node)2170 void InstructionSelector::VisitUint32LessThanOrEqual(Node* node) {
2171 FlagsContinuation cont =
2172 FlagsContinuation::ForSet(kUnsignedLessThanOrEqual, node);
2173 VisitWordCompare(this, node, &cont);
2174 }
2175
VisitInt32AddWithOverflow(Node * node)2176 void InstructionSelector::VisitInt32AddWithOverflow(Node* node) {
2177 if (Node* ovf = NodeProperties::FindProjection(node, 1)) {
2178 FlagsContinuation cont = FlagsContinuation::ForSet(kOverflow, ovf);
2179 return VisitBinop(this, node, kArmAdd, kArmAdd, &cont);
2180 }
2181 FlagsContinuation cont;
2182 VisitBinop(this, node, kArmAdd, kArmAdd, &cont);
2183 }
2184
VisitInt32SubWithOverflow(Node * node)2185 void InstructionSelector::VisitInt32SubWithOverflow(Node* node) {
2186 if (Node* ovf = NodeProperties::FindProjection(node, 1)) {
2187 FlagsContinuation cont = FlagsContinuation::ForSet(kOverflow, ovf);
2188 return VisitBinop(this, node, kArmSub, kArmRsb, &cont);
2189 }
2190 FlagsContinuation cont;
2191 VisitBinop(this, node, kArmSub, kArmRsb, &cont);
2192 }
2193
VisitInt32MulWithOverflow(Node * node)2194 void InstructionSelector::VisitInt32MulWithOverflow(Node* node) {
2195 if (Node* ovf = NodeProperties::FindProjection(node, 1)) {
2196 // ARM doesn't set the overflow flag for multiplication, so we need to test
2197 // on kNotEqual. Here is the code sequence used:
2198 // smull resultlow, resulthigh, left, right
2199 // cmp resulthigh, Operand(resultlow, ASR, 31)
2200 FlagsContinuation cont = FlagsContinuation::ForSet(kNotEqual, ovf);
2201 return EmitInt32MulWithOverflow(this, node, &cont);
2202 }
2203 FlagsContinuation cont;
2204 EmitInt32MulWithOverflow(this, node, &cont);
2205 }
2206
VisitFloat32Equal(Node * node)2207 void InstructionSelector::VisitFloat32Equal(Node* node) {
2208 FlagsContinuation cont = FlagsContinuation::ForSet(kEqual, node);
2209 VisitFloat32Compare(this, node, &cont);
2210 }
2211
VisitFloat32LessThan(Node * node)2212 void InstructionSelector::VisitFloat32LessThan(Node* node) {
2213 FlagsContinuation cont = FlagsContinuation::ForSet(kFloatLessThan, node);
2214 VisitFloat32Compare(this, node, &cont);
2215 }
2216
VisitFloat32LessThanOrEqual(Node * node)2217 void InstructionSelector::VisitFloat32LessThanOrEqual(Node* node) {
2218 FlagsContinuation cont =
2219 FlagsContinuation::ForSet(kFloatLessThanOrEqual, node);
2220 VisitFloat32Compare(this, node, &cont);
2221 }
2222
VisitFloat64Equal(Node * node)2223 void InstructionSelector::VisitFloat64Equal(Node* node) {
2224 FlagsContinuation cont = FlagsContinuation::ForSet(kEqual, node);
2225 VisitFloat64Compare(this, node, &cont);
2226 }
2227
VisitFloat64LessThan(Node * node)2228 void InstructionSelector::VisitFloat64LessThan(Node* node) {
2229 FlagsContinuation cont = FlagsContinuation::ForSet(kFloatLessThan, node);
2230 VisitFloat64Compare(this, node, &cont);
2231 }
2232
VisitFloat64LessThanOrEqual(Node * node)2233 void InstructionSelector::VisitFloat64LessThanOrEqual(Node* node) {
2234 FlagsContinuation cont =
2235 FlagsContinuation::ForSet(kFloatLessThanOrEqual, node);
2236 VisitFloat64Compare(this, node, &cont);
2237 }
2238
VisitFloat64InsertLowWord32(Node * node)2239 void InstructionSelector::VisitFloat64InsertLowWord32(Node* node) {
2240 ArmOperandGenerator g(this);
2241 Node* left = node->InputAt(0);
2242 Node* right = node->InputAt(1);
2243 if (left->opcode() == IrOpcode::kFloat64InsertHighWord32 &&
2244 CanCover(node, left)) {
2245 left = left->InputAt(1);
2246 Emit(kArmVmovF64U32U32, g.DefineAsRegister(node), g.UseRegister(right),
2247 g.UseRegister(left));
2248 return;
2249 }
2250 Emit(kArmVmovLowF64U32, g.DefineSameAsFirst(node), g.UseRegister(left),
2251 g.UseRegister(right));
2252 }
2253
VisitFloat64InsertHighWord32(Node * node)2254 void InstructionSelector::VisitFloat64InsertHighWord32(Node* node) {
2255 ArmOperandGenerator g(this);
2256 Node* left = node->InputAt(0);
2257 Node* right = node->InputAt(1);
2258 if (left->opcode() == IrOpcode::kFloat64InsertLowWord32 &&
2259 CanCover(node, left)) {
2260 left = left->InputAt(1);
2261 Emit(kArmVmovF64U32U32, g.DefineAsRegister(node), g.UseRegister(left),
2262 g.UseRegister(right));
2263 return;
2264 }
2265 Emit(kArmVmovHighF64U32, g.DefineSameAsFirst(node), g.UseRegister(left),
2266 g.UseRegister(right));
2267 }
2268
VisitMemoryBarrier(Node * node)2269 void InstructionSelector::VisitMemoryBarrier(Node* node) {
2270 ArmOperandGenerator g(this);
2271 Emit(kArmDmbIsh, g.NoOutput());
2272 }
2273
VisitWord32AtomicLoad(Node * node)2274 void InstructionSelector::VisitWord32AtomicLoad(Node* node) {
2275 // The memory order is ignored as both acquire and sequentially consistent
2276 // loads can emit LDR; DMB ISH.
2277 // https://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html
2278 AtomicLoadParameters atomic_load_params = AtomicLoadParametersOf(node->op());
2279 LoadRepresentation load_rep = atomic_load_params.representation();
2280 ArmOperandGenerator g(this);
2281 Node* base = node->InputAt(0);
2282 Node* index = node->InputAt(1);
2283 ArchOpcode opcode;
2284 switch (load_rep.representation()) {
2285 case MachineRepresentation::kWord8:
2286 opcode = load_rep.IsSigned() ? kAtomicLoadInt8 : kAtomicLoadUint8;
2287 break;
2288 case MachineRepresentation::kWord16:
2289 opcode = load_rep.IsSigned() ? kAtomicLoadInt16 : kAtomicLoadUint16;
2290 break;
2291 case MachineRepresentation::kTaggedSigned: // Fall through.
2292 case MachineRepresentation::kTaggedPointer: // Fall through.
2293 case MachineRepresentation::kTagged: // Fall through.
2294 case MachineRepresentation::kWord32:
2295 opcode = kAtomicLoadWord32;
2296 break;
2297 default:
2298 UNREACHABLE();
2299 }
2300 Emit(opcode | AddressingModeField::encode(kMode_Offset_RR),
2301 g.DefineAsRegister(node), g.UseRegister(base), g.UseRegister(index));
2302 }
2303
VisitWord32AtomicStore(Node * node)2304 void InstructionSelector::VisitWord32AtomicStore(Node* node) {
2305 AtomicStoreParameters store_params = AtomicStoreParametersOf(node->op());
2306 VisitStoreCommon(this, node, store_params.store_representation(),
2307 store_params.order());
2308 }
2309
VisitWord32AtomicExchange(Node * node)2310 void InstructionSelector::VisitWord32AtomicExchange(Node* node) {
2311 ArmOperandGenerator g(this);
2312 Node* base = node->InputAt(0);
2313 Node* index = node->InputAt(1);
2314 Node* value = node->InputAt(2);
2315 ArchOpcode opcode;
2316 MachineType type = AtomicOpType(node->op());
2317 if (type == MachineType::Int8()) {
2318 opcode = kAtomicExchangeInt8;
2319 } else if (type == MachineType::Uint8()) {
2320 opcode = kAtomicExchangeUint8;
2321 } else if (type == MachineType::Int16()) {
2322 opcode = kAtomicExchangeInt16;
2323 } else if (type == MachineType::Uint16()) {
2324 opcode = kAtomicExchangeUint16;
2325 } else if (type == MachineType::Int32() || type == MachineType::Uint32()) {
2326 opcode = kAtomicExchangeWord32;
2327 } else {
2328 UNREACHABLE();
2329 }
2330
2331 AddressingMode addressing_mode = kMode_Offset_RR;
2332 InstructionOperand inputs[3];
2333 size_t input_count = 0;
2334 inputs[input_count++] = g.UseRegister(base);
2335 inputs[input_count++] = g.UseRegister(index);
2336 inputs[input_count++] = g.UseUniqueRegister(value);
2337 InstructionOperand outputs[1];
2338 outputs[0] = g.DefineAsRegister(node);
2339 InstructionOperand temps[] = {g.TempRegister(), g.TempRegister()};
2340 InstructionCode code = opcode | AddressingModeField::encode(addressing_mode);
2341 Emit(code, 1, outputs, input_count, inputs, arraysize(temps), temps);
2342 }
2343
VisitWord32AtomicCompareExchange(Node * node)2344 void InstructionSelector::VisitWord32AtomicCompareExchange(Node* node) {
2345 ArmOperandGenerator g(this);
2346 Node* base = node->InputAt(0);
2347 Node* index = node->InputAt(1);
2348 Node* old_value = node->InputAt(2);
2349 Node* new_value = node->InputAt(3);
2350 ArchOpcode opcode;
2351 MachineType type = AtomicOpType(node->op());
2352 if (type == MachineType::Int8()) {
2353 opcode = kAtomicCompareExchangeInt8;
2354 } else if (type == MachineType::Uint8()) {
2355 opcode = kAtomicCompareExchangeUint8;
2356 } else if (type == MachineType::Int16()) {
2357 opcode = kAtomicCompareExchangeInt16;
2358 } else if (type == MachineType::Uint16()) {
2359 opcode = kAtomicCompareExchangeUint16;
2360 } else if (type == MachineType::Int32() || type == MachineType::Uint32()) {
2361 opcode = kAtomicCompareExchangeWord32;
2362 } else {
2363 UNREACHABLE();
2364 }
2365
2366 AddressingMode addressing_mode = kMode_Offset_RR;
2367 InstructionOperand inputs[4];
2368 size_t input_count = 0;
2369 inputs[input_count++] = g.UseRegister(base);
2370 inputs[input_count++] = g.UseRegister(index);
2371 inputs[input_count++] = g.UseUniqueRegister(old_value);
2372 inputs[input_count++] = g.UseUniqueRegister(new_value);
2373 InstructionOperand outputs[1];
2374 outputs[0] = g.DefineAsRegister(node);
2375 InstructionOperand temps[] = {g.TempRegister(), g.TempRegister(),
2376 g.TempRegister()};
2377 InstructionCode code = opcode | AddressingModeField::encode(addressing_mode);
2378 Emit(code, 1, outputs, input_count, inputs, arraysize(temps), temps);
2379 }
2380
VisitWord32AtomicBinaryOperation(Node * node,ArchOpcode int8_op,ArchOpcode uint8_op,ArchOpcode int16_op,ArchOpcode uint16_op,ArchOpcode word32_op)2381 void InstructionSelector::VisitWord32AtomicBinaryOperation(
2382 Node* node, ArchOpcode int8_op, ArchOpcode uint8_op, ArchOpcode int16_op,
2383 ArchOpcode uint16_op, ArchOpcode word32_op) {
2384 ArmOperandGenerator g(this);
2385 Node* base = node->InputAt(0);
2386 Node* index = node->InputAt(1);
2387 Node* value = node->InputAt(2);
2388 ArchOpcode opcode;
2389 MachineType type = AtomicOpType(node->op());
2390 if (type == MachineType::Int8()) {
2391 opcode = int8_op;
2392 } else if (type == MachineType::Uint8()) {
2393 opcode = uint8_op;
2394 } else if (type == MachineType::Int16()) {
2395 opcode = int16_op;
2396 } else if (type == MachineType::Uint16()) {
2397 opcode = uint16_op;
2398 } else if (type == MachineType::Int32() || type == MachineType::Uint32()) {
2399 opcode = word32_op;
2400 } else {
2401 UNREACHABLE();
2402 }
2403
2404 AddressingMode addressing_mode = kMode_Offset_RR;
2405 InstructionOperand inputs[3];
2406 size_t input_count = 0;
2407 inputs[input_count++] = g.UseRegister(base);
2408 inputs[input_count++] = g.UseRegister(index);
2409 inputs[input_count++] = g.UseUniqueRegister(value);
2410 InstructionOperand outputs[1];
2411 outputs[0] = g.DefineAsRegister(node);
2412 InstructionOperand temps[] = {g.TempRegister(), g.TempRegister(),
2413 g.TempRegister()};
2414 InstructionCode code = opcode | AddressingModeField::encode(addressing_mode);
2415 Emit(code, 1, outputs, input_count, inputs, arraysize(temps), temps);
2416 }
2417
2418 #define VISIT_ATOMIC_BINOP(op) \
2419 void InstructionSelector::VisitWord32Atomic##op(Node* node) { \
2420 VisitWord32AtomicBinaryOperation( \
2421 node, kAtomic##op##Int8, kAtomic##op##Uint8, kAtomic##op##Int16, \
2422 kAtomic##op##Uint16, kAtomic##op##Word32); \
2423 }
2424 VISIT_ATOMIC_BINOP(Add)
VISIT_ATOMIC_BINOP(Sub)2425 VISIT_ATOMIC_BINOP(Sub)
2426 VISIT_ATOMIC_BINOP(And)
2427 VISIT_ATOMIC_BINOP(Or)
2428 VISIT_ATOMIC_BINOP(Xor)
2429 #undef VISIT_ATOMIC_BINOP
2430
2431 void InstructionSelector::VisitWord32AtomicPairLoad(Node* node) {
2432 ArmOperandGenerator g(this);
2433 Node* base = node->InputAt(0);
2434 Node* index = node->InputAt(1);
2435 InstructionOperand inputs[3];
2436 size_t input_count = 0;
2437 inputs[input_count++] = g.UseUniqueRegister(base);
2438 inputs[input_count++] = g.UseUniqueRegister(index);
2439 InstructionOperand temps[1];
2440 size_t temp_count = 0;
2441 InstructionOperand outputs[2];
2442 size_t output_count = 0;
2443
2444 Node* projection0 = NodeProperties::FindProjection(node, 0);
2445 Node* projection1 = NodeProperties::FindProjection(node, 1);
2446 if (projection0 && projection1) {
2447 outputs[output_count++] = g.DefineAsFixed(projection0, r0);
2448 outputs[output_count++] = g.DefineAsFixed(projection1, r1);
2449 temps[temp_count++] = g.TempRegister();
2450 } else if (projection0) {
2451 inputs[input_count++] = g.UseImmediate(0);
2452 outputs[output_count++] = g.DefineAsRegister(projection0);
2453 } else if (projection1) {
2454 inputs[input_count++] = g.UseImmediate(4);
2455 temps[temp_count++] = g.TempRegister();
2456 outputs[output_count++] = g.DefineAsRegister(projection1);
2457 } else {
2458 // There is no use of the loaded value, we don't need to generate code.
2459 return;
2460 }
2461 Emit(kArmWord32AtomicPairLoad, output_count, outputs, input_count, inputs,
2462 temp_count, temps);
2463 }
2464
VisitWord32AtomicPairStore(Node * node)2465 void InstructionSelector::VisitWord32AtomicPairStore(Node* node) {
2466 ArmOperandGenerator g(this);
2467 Node* base = node->InputAt(0);
2468 Node* index = node->InputAt(1);
2469 Node* value_low = node->InputAt(2);
2470 Node* value_high = node->InputAt(3);
2471 AddressingMode addressing_mode = kMode_Offset_RR;
2472 InstructionOperand inputs[] = {
2473 g.UseUniqueRegister(base), g.UseUniqueRegister(index),
2474 g.UseFixed(value_low, r2), g.UseFixed(value_high, r3)};
2475 InstructionOperand temps[] = {g.TempRegister(), g.TempRegister(r0),
2476 g.TempRegister(r1)};
2477 InstructionCode code =
2478 kArmWord32AtomicPairStore | AddressingModeField::encode(addressing_mode);
2479 Emit(code, 0, nullptr, arraysize(inputs), inputs, arraysize(temps), temps);
2480 }
2481
VisitWord32AtomicPairAdd(Node * node)2482 void InstructionSelector::VisitWord32AtomicPairAdd(Node* node) {
2483 VisitPairAtomicBinOp(this, node, kArmWord32AtomicPairAdd);
2484 }
2485
VisitWord32AtomicPairSub(Node * node)2486 void InstructionSelector::VisitWord32AtomicPairSub(Node* node) {
2487 VisitPairAtomicBinOp(this, node, kArmWord32AtomicPairSub);
2488 }
2489
VisitWord32AtomicPairAnd(Node * node)2490 void InstructionSelector::VisitWord32AtomicPairAnd(Node* node) {
2491 VisitPairAtomicBinOp(this, node, kArmWord32AtomicPairAnd);
2492 }
2493
VisitWord32AtomicPairOr(Node * node)2494 void InstructionSelector::VisitWord32AtomicPairOr(Node* node) {
2495 VisitPairAtomicBinOp(this, node, kArmWord32AtomicPairOr);
2496 }
2497
VisitWord32AtomicPairXor(Node * node)2498 void InstructionSelector::VisitWord32AtomicPairXor(Node* node) {
2499 VisitPairAtomicBinOp(this, node, kArmWord32AtomicPairXor);
2500 }
2501
VisitWord32AtomicPairExchange(Node * node)2502 void InstructionSelector::VisitWord32AtomicPairExchange(Node* node) {
2503 ArmOperandGenerator g(this);
2504 Node* base = node->InputAt(0);
2505 Node* index = node->InputAt(1);
2506 Node* value = node->InputAt(2);
2507 Node* value_high = node->InputAt(3);
2508 AddressingMode addressing_mode = kMode_Offset_RR;
2509 InstructionOperand inputs[] = {
2510 g.UseFixed(value, r0), g.UseFixed(value_high, r1),
2511 g.UseUniqueRegister(base), g.UseUniqueRegister(index)};
2512 InstructionCode code = kArmWord32AtomicPairExchange |
2513 AddressingModeField::encode(addressing_mode);
2514 Node* projection0 = NodeProperties::FindProjection(node, 0);
2515 Node* projection1 = NodeProperties::FindProjection(node, 1);
2516 InstructionOperand outputs[2];
2517 size_t output_count = 0;
2518 InstructionOperand temps[4];
2519 size_t temp_count = 0;
2520 temps[temp_count++] = g.TempRegister();
2521 temps[temp_count++] = g.TempRegister();
2522 if (projection0) {
2523 outputs[output_count++] = g.DefineAsFixed(projection0, r6);
2524 } else {
2525 temps[temp_count++] = g.TempRegister(r6);
2526 }
2527 if (projection1) {
2528 outputs[output_count++] = g.DefineAsFixed(projection1, r7);
2529 } else {
2530 temps[temp_count++] = g.TempRegister(r7);
2531 }
2532 Emit(code, output_count, outputs, arraysize(inputs), inputs, temp_count,
2533 temps);
2534 }
2535
VisitWord32AtomicPairCompareExchange(Node * node)2536 void InstructionSelector::VisitWord32AtomicPairCompareExchange(Node* node) {
2537 ArmOperandGenerator g(this);
2538 AddressingMode addressing_mode = kMode_Offset_RR;
2539 InstructionOperand inputs[] = {g.UseFixed(node->InputAt(2), r4),
2540 g.UseFixed(node->InputAt(3), r5),
2541 g.UseFixed(node->InputAt(4), r8),
2542 g.UseFixed(node->InputAt(5), r9),
2543 g.UseUniqueRegister(node->InputAt(0)),
2544 g.UseUniqueRegister(node->InputAt(1))};
2545 InstructionCode code = kArmWord32AtomicPairCompareExchange |
2546 AddressingModeField::encode(addressing_mode);
2547 Node* projection0 = NodeProperties::FindProjection(node, 0);
2548 Node* projection1 = NodeProperties::FindProjection(node, 1);
2549 InstructionOperand outputs[2];
2550 size_t output_count = 0;
2551 InstructionOperand temps[4];
2552 size_t temp_count = 0;
2553 temps[temp_count++] = g.TempRegister();
2554 temps[temp_count++] = g.TempRegister();
2555 if (projection0) {
2556 outputs[output_count++] = g.DefineAsFixed(projection0, r2);
2557 } else {
2558 temps[temp_count++] = g.TempRegister(r2);
2559 }
2560 if (projection1) {
2561 outputs[output_count++] = g.DefineAsFixed(projection1, r3);
2562 } else {
2563 temps[temp_count++] = g.TempRegister(r3);
2564 }
2565 Emit(code, output_count, outputs, arraysize(inputs), inputs, temp_count,
2566 temps);
2567 }
2568
2569 #define SIMD_TYPE_LIST(V) \
2570 V(F32x4) \
2571 V(I32x4) \
2572 V(I16x8) \
2573 V(I8x16)
2574
2575 #define SIMD_UNOP_LIST(V) \
2576 V(F64x2Abs, kArmF64x2Abs) \
2577 V(F64x2Neg, kArmF64x2Neg) \
2578 V(F64x2Sqrt, kArmF64x2Sqrt) \
2579 V(F32x4SConvertI32x4, kArmF32x4SConvertI32x4) \
2580 V(F32x4UConvertI32x4, kArmF32x4UConvertI32x4) \
2581 V(F32x4Abs, kArmF32x4Abs) \
2582 V(F32x4Neg, kArmF32x4Neg) \
2583 V(F32x4RecipApprox, kArmF32x4RecipApprox) \
2584 V(F32x4RecipSqrtApprox, kArmF32x4RecipSqrtApprox) \
2585 V(I64x2Abs, kArmI64x2Abs) \
2586 V(I64x2SConvertI32x4Low, kArmI64x2SConvertI32x4Low) \
2587 V(I64x2SConvertI32x4High, kArmI64x2SConvertI32x4High) \
2588 V(I64x2UConvertI32x4Low, kArmI64x2UConvertI32x4Low) \
2589 V(I64x2UConvertI32x4High, kArmI64x2UConvertI32x4High) \
2590 V(I32x4SConvertF32x4, kArmI32x4SConvertF32x4) \
2591 V(I32x4RelaxedTruncF32x4S, kArmI32x4SConvertF32x4) \
2592 V(I32x4SConvertI16x8Low, kArmI32x4SConvertI16x8Low) \
2593 V(I32x4SConvertI16x8High, kArmI32x4SConvertI16x8High) \
2594 V(I32x4Neg, kArmI32x4Neg) \
2595 V(I32x4UConvertF32x4, kArmI32x4UConvertF32x4) \
2596 V(I32x4RelaxedTruncF32x4U, kArmI32x4UConvertF32x4) \
2597 V(I32x4UConvertI16x8Low, kArmI32x4UConvertI16x8Low) \
2598 V(I32x4UConvertI16x8High, kArmI32x4UConvertI16x8High) \
2599 V(I32x4Abs, kArmI32x4Abs) \
2600 V(I16x8SConvertI8x16Low, kArmI16x8SConvertI8x16Low) \
2601 V(I16x8SConvertI8x16High, kArmI16x8SConvertI8x16High) \
2602 V(I16x8Neg, kArmI16x8Neg) \
2603 V(I16x8UConvertI8x16Low, kArmI16x8UConvertI8x16Low) \
2604 V(I16x8UConvertI8x16High, kArmI16x8UConvertI8x16High) \
2605 V(I16x8Abs, kArmI16x8Abs) \
2606 V(I8x16Neg, kArmI8x16Neg) \
2607 V(I8x16Abs, kArmI8x16Abs) \
2608 V(I8x16Popcnt, kArmVcnt) \
2609 V(S128Not, kArmS128Not) \
2610 V(I64x2AllTrue, kArmI64x2AllTrue) \
2611 V(I32x4AllTrue, kArmI32x4AllTrue) \
2612 V(I16x8AllTrue, kArmI16x8AllTrue) \
2613 V(V128AnyTrue, kArmV128AnyTrue) \
2614 V(I8x16AllTrue, kArmI8x16AllTrue)
2615
2616 #define SIMD_SHIFT_OP_LIST(V) \
2617 V(I64x2Shl, 64) \
2618 V(I64x2ShrS, 64) \
2619 V(I64x2ShrU, 64) \
2620 V(I32x4Shl, 32) \
2621 V(I32x4ShrS, 32) \
2622 V(I32x4ShrU, 32) \
2623 V(I16x8Shl, 16) \
2624 V(I16x8ShrS, 16) \
2625 V(I16x8ShrU, 16) \
2626 V(I8x16Shl, 8) \
2627 V(I8x16ShrS, 8) \
2628 V(I8x16ShrU, 8)
2629
2630 #define SIMD_BINOP_LIST(V) \
2631 V(F64x2Add, kArmF64x2Add) \
2632 V(F64x2Sub, kArmF64x2Sub) \
2633 V(F64x2Mul, kArmF64x2Mul) \
2634 V(F64x2Div, kArmF64x2Div) \
2635 V(F64x2Min, kArmF64x2Min) \
2636 V(F64x2Max, kArmF64x2Max) \
2637 V(F64x2Eq, kArmF64x2Eq) \
2638 V(F64x2Ne, kArmF64x2Ne) \
2639 V(F64x2Lt, kArmF64x2Lt) \
2640 V(F64x2Le, kArmF64x2Le) \
2641 V(F32x4Add, kArmF32x4Add) \
2642 V(F32x4Sub, kArmF32x4Sub) \
2643 V(F32x4Mul, kArmF32x4Mul) \
2644 V(F32x4Min, kArmF32x4Min) \
2645 V(F32x4RelaxedMin, kArmF32x4Min) \
2646 V(F32x4Max, kArmF32x4Max) \
2647 V(F32x4RelaxedMax, kArmF32x4Max) \
2648 V(F32x4Eq, kArmF32x4Eq) \
2649 V(F32x4Ne, kArmF32x4Ne) \
2650 V(F32x4Lt, kArmF32x4Lt) \
2651 V(F32x4Le, kArmF32x4Le) \
2652 V(I64x2Add, kArmI64x2Add) \
2653 V(I64x2Sub, kArmI64x2Sub) \
2654 V(I32x4Sub, kArmI32x4Sub) \
2655 V(I32x4Mul, kArmI32x4Mul) \
2656 V(I32x4MinS, kArmI32x4MinS) \
2657 V(I32x4MaxS, kArmI32x4MaxS) \
2658 V(I32x4Eq, kArmI32x4Eq) \
2659 V(I64x2Eq, kArmI64x2Eq) \
2660 V(I64x2Ne, kArmI64x2Ne) \
2661 V(I64x2GtS, kArmI64x2GtS) \
2662 V(I64x2GeS, kArmI64x2GeS) \
2663 V(I32x4Ne, kArmI32x4Ne) \
2664 V(I32x4GtS, kArmI32x4GtS) \
2665 V(I32x4GeS, kArmI32x4GeS) \
2666 V(I32x4MinU, kArmI32x4MinU) \
2667 V(I32x4MaxU, kArmI32x4MaxU) \
2668 V(I32x4GtU, kArmI32x4GtU) \
2669 V(I32x4GeU, kArmI32x4GeU) \
2670 V(I16x8SConvertI32x4, kArmI16x8SConvertI32x4) \
2671 V(I16x8AddSatS, kArmI16x8AddSatS) \
2672 V(I16x8Sub, kArmI16x8Sub) \
2673 V(I16x8SubSatS, kArmI16x8SubSatS) \
2674 V(I16x8Mul, kArmI16x8Mul) \
2675 V(I16x8MinS, kArmI16x8MinS) \
2676 V(I16x8MaxS, kArmI16x8MaxS) \
2677 V(I16x8Eq, kArmI16x8Eq) \
2678 V(I16x8Ne, kArmI16x8Ne) \
2679 V(I16x8GtS, kArmI16x8GtS) \
2680 V(I16x8GeS, kArmI16x8GeS) \
2681 V(I16x8UConvertI32x4, kArmI16x8UConvertI32x4) \
2682 V(I16x8AddSatU, kArmI16x8AddSatU) \
2683 V(I16x8SubSatU, kArmI16x8SubSatU) \
2684 V(I16x8MinU, kArmI16x8MinU) \
2685 V(I16x8MaxU, kArmI16x8MaxU) \
2686 V(I16x8GtU, kArmI16x8GtU) \
2687 V(I16x8GeU, kArmI16x8GeU) \
2688 V(I16x8RoundingAverageU, kArmI16x8RoundingAverageU) \
2689 V(I16x8Q15MulRSatS, kArmI16x8Q15MulRSatS) \
2690 V(I8x16SConvertI16x8, kArmI8x16SConvertI16x8) \
2691 V(I8x16Add, kArmI8x16Add) \
2692 V(I8x16AddSatS, kArmI8x16AddSatS) \
2693 V(I8x16Sub, kArmI8x16Sub) \
2694 V(I8x16SubSatS, kArmI8x16SubSatS) \
2695 V(I8x16MinS, kArmI8x16MinS) \
2696 V(I8x16MaxS, kArmI8x16MaxS) \
2697 V(I8x16Eq, kArmI8x16Eq) \
2698 V(I8x16Ne, kArmI8x16Ne) \
2699 V(I8x16GtS, kArmI8x16GtS) \
2700 V(I8x16GeS, kArmI8x16GeS) \
2701 V(I8x16UConvertI16x8, kArmI8x16UConvertI16x8) \
2702 V(I8x16AddSatU, kArmI8x16AddSatU) \
2703 V(I8x16SubSatU, kArmI8x16SubSatU) \
2704 V(I8x16MinU, kArmI8x16MinU) \
2705 V(I8x16MaxU, kArmI8x16MaxU) \
2706 V(I8x16GtU, kArmI8x16GtU) \
2707 V(I8x16GeU, kArmI8x16GeU) \
2708 V(I8x16RoundingAverageU, kArmI8x16RoundingAverageU) \
2709 V(S128And, kArmS128And) \
2710 V(S128Or, kArmS128Or) \
2711 V(S128Xor, kArmS128Xor) \
2712 V(S128AndNot, kArmS128AndNot)
2713
VisitI32x4DotI16x8S(Node * node)2714 void InstructionSelector::VisitI32x4DotI16x8S(Node* node) {
2715 ArmOperandGenerator g(this);
2716 InstructionOperand temps[] = {g.TempSimd128Register()};
2717 Emit(kArmI32x4DotI16x8S, g.DefineAsRegister(node),
2718 g.UseUniqueRegister(node->InputAt(0)),
2719 g.UseUniqueRegister(node->InputAt(1)), arraysize(temps), temps);
2720 }
2721
VisitS128Const(Node * node)2722 void InstructionSelector::VisitS128Const(Node* node) {
2723 ArmOperandGenerator g(this);
2724 uint32_t val[kSimd128Size / sizeof(uint32_t)];
2725 memcpy(val, S128ImmediateParameterOf(node->op()).data(), kSimd128Size);
2726 // If all bytes are zeros, avoid emitting code for generic constants.
2727 bool all_zeros = !(val[0] || val[1] || val[2] || val[3]);
2728 bool all_ones = val[0] == UINT32_MAX && val[1] == UINT32_MAX &&
2729 val[2] == UINT32_MAX && val[3] == UINT32_MAX;
2730 InstructionOperand dst = g.DefineAsRegister(node);
2731 if (all_zeros) {
2732 Emit(kArmS128Zero, dst);
2733 } else if (all_ones) {
2734 Emit(kArmS128AllOnes, dst);
2735 } else {
2736 Emit(kArmS128Const, dst, g.UseImmediate(val[0]), g.UseImmediate(val[1]),
2737 g.UseImmediate(val[2]), g.UseImmediate(val[3]));
2738 }
2739 }
2740
VisitS128Zero(Node * node)2741 void InstructionSelector::VisitS128Zero(Node* node) {
2742 ArmOperandGenerator g(this);
2743 Emit(kArmS128Zero, g.DefineAsRegister(node));
2744 }
2745
2746 #define SIMD_VISIT_SPLAT(Type) \
2747 void InstructionSelector::Visit##Type##Splat(Node* node) { \
2748 VisitRR(this, kArm##Type##Splat, node); \
2749 }
2750 SIMD_TYPE_LIST(SIMD_VISIT_SPLAT)
SIMD_VISIT_SPLAT(F64x2)2751 SIMD_VISIT_SPLAT(F64x2)
2752 #undef SIMD_VISIT_SPLAT
2753
2754 #define SIMD_VISIT_EXTRACT_LANE(Type, Sign) \
2755 void InstructionSelector::Visit##Type##ExtractLane##Sign(Node* node) { \
2756 VisitRRI(this, kArm##Type##ExtractLane##Sign, node); \
2757 }
2758 SIMD_VISIT_EXTRACT_LANE(F64x2, )
2759 SIMD_VISIT_EXTRACT_LANE(F32x4, )
2760 SIMD_VISIT_EXTRACT_LANE(I32x4, )
2761 SIMD_VISIT_EXTRACT_LANE(I16x8, U)
2762 SIMD_VISIT_EXTRACT_LANE(I16x8, S)
2763 SIMD_VISIT_EXTRACT_LANE(I8x16, U)
2764 SIMD_VISIT_EXTRACT_LANE(I8x16, S)
2765 #undef SIMD_VISIT_EXTRACT_LANE
2766
2767 #define SIMD_VISIT_REPLACE_LANE(Type) \
2768 void InstructionSelector::Visit##Type##ReplaceLane(Node* node) { \
2769 VisitRRIR(this, kArm##Type##ReplaceLane, node); \
2770 }
2771 SIMD_TYPE_LIST(SIMD_VISIT_REPLACE_LANE)
2772 SIMD_VISIT_REPLACE_LANE(F64x2)
2773 #undef SIMD_VISIT_REPLACE_LANE
2774 #undef SIMD_TYPE_LIST
2775
2776 #define SIMD_VISIT_UNOP(Name, instruction) \
2777 void InstructionSelector::Visit##Name(Node* node) { \
2778 VisitRR(this, instruction, node); \
2779 }
2780 SIMD_UNOP_LIST(SIMD_VISIT_UNOP)
2781 #undef SIMD_VISIT_UNOP
2782 #undef SIMD_UNOP_LIST
2783
2784 #define SIMD_VISIT_SHIFT_OP(Name, width) \
2785 void InstructionSelector::Visit##Name(Node* node) { \
2786 VisitSimdShiftRRR(this, kArm##Name, node, width); \
2787 }
2788 SIMD_SHIFT_OP_LIST(SIMD_VISIT_SHIFT_OP)
2789 #undef SIMD_VISIT_SHIFT_OP
2790 #undef SIMD_SHIFT_OP_LIST
2791
2792 #define SIMD_VISIT_BINOP(Name, instruction) \
2793 void InstructionSelector::Visit##Name(Node* node) { \
2794 VisitRRR(this, instruction, node); \
2795 }
2796 SIMD_BINOP_LIST(SIMD_VISIT_BINOP)
2797 #undef SIMD_VISIT_BINOP
2798 #undef SIMD_BINOP_LIST
2799
2800 #define VISIT_SIMD_ADD(Type, PairwiseType, NeonWidth) \
2801 void InstructionSelector::Visit##Type##Add(Node* node) { \
2802 ArmOperandGenerator g(this); \
2803 Node* left = node->InputAt(0); \
2804 Node* right = node->InputAt(1); \
2805 if (left->opcode() == \
2806 IrOpcode::k##Type##ExtAddPairwise##PairwiseType##S && \
2807 CanCover(node, left)) { \
2808 Emit(kArmVpadal | MiscField::encode(NeonS##NeonWidth), \
2809 g.DefineSameAsFirst(node), g.UseRegister(right), \
2810 g.UseRegister(left->InputAt(0))); \
2811 return; \
2812 } \
2813 if (left->opcode() == \
2814 IrOpcode::k##Type##ExtAddPairwise##PairwiseType##U && \
2815 CanCover(node, left)) { \
2816 Emit(kArmVpadal | MiscField::encode(NeonU##NeonWidth), \
2817 g.DefineSameAsFirst(node), g.UseRegister(right), \
2818 g.UseRegister(left->InputAt(0))); \
2819 return; \
2820 } \
2821 if (right->opcode() == \
2822 IrOpcode::k##Type##ExtAddPairwise##PairwiseType##S && \
2823 CanCover(node, right)) { \
2824 Emit(kArmVpadal | MiscField::encode(NeonS##NeonWidth), \
2825 g.DefineSameAsFirst(node), g.UseRegister(left), \
2826 g.UseRegister(right->InputAt(0))); \
2827 return; \
2828 } \
2829 if (right->opcode() == \
2830 IrOpcode::k##Type##ExtAddPairwise##PairwiseType##U && \
2831 CanCover(node, right)) { \
2832 Emit(kArmVpadal | MiscField::encode(NeonU##NeonWidth), \
2833 g.DefineSameAsFirst(node), g.UseRegister(left), \
2834 g.UseRegister(right->InputAt(0))); \
2835 return; \
2836 } \
2837 VisitRRR(this, kArm##Type##Add, node); \
2838 }
2839
2840 VISIT_SIMD_ADD(I16x8, I8x16, 8)
2841 VISIT_SIMD_ADD(I32x4, I16x8, 16)
2842 #undef VISIT_SIMD_ADD
2843
2844 void InstructionSelector::VisitI64x2SplatI32Pair(Node* node) {
2845 ArmOperandGenerator g(this);
2846 InstructionOperand operand0 = g.UseRegister(node->InputAt(0));
2847 InstructionOperand operand1 = g.UseRegister(node->InputAt(1));
2848 Emit(kArmI64x2SplatI32Pair, g.DefineAsRegister(node), operand0, operand1);
2849 }
2850
VisitI64x2ReplaceLaneI32Pair(Node * node)2851 void InstructionSelector::VisitI64x2ReplaceLaneI32Pair(Node* node) {
2852 ArmOperandGenerator g(this);
2853 InstructionOperand operand = g.UseRegister(node->InputAt(0));
2854 InstructionOperand lane = g.UseImmediate(OpParameter<int32_t>(node->op()));
2855 InstructionOperand low = g.UseRegister(node->InputAt(1));
2856 InstructionOperand high = g.UseRegister(node->InputAt(2));
2857 Emit(kArmI64x2ReplaceLaneI32Pair, g.DefineSameAsFirst(node), operand, lane,
2858 low, high);
2859 }
2860
VisitI64x2Neg(Node * node)2861 void InstructionSelector::VisitI64x2Neg(Node* node) {
2862 ArmOperandGenerator g(this);
2863 Emit(kArmI64x2Neg, g.DefineAsRegister(node),
2864 g.UseUniqueRegister(node->InputAt(0)));
2865 }
2866
VisitI64x2Mul(Node * node)2867 void InstructionSelector::VisitI64x2Mul(Node* node) {
2868 ArmOperandGenerator g(this);
2869 InstructionOperand temps[] = {g.TempSimd128Register()};
2870 Emit(kArmI64x2Mul, g.DefineAsRegister(node),
2871 g.UseUniqueRegister(node->InputAt(0)),
2872 g.UseUniqueRegister(node->InputAt(1)), arraysize(temps), temps);
2873 }
2874
VisitF32x4Sqrt(Node * node)2875 void InstructionSelector::VisitF32x4Sqrt(Node* node) {
2876 ArmOperandGenerator g(this);
2877 // Use fixed registers in the lower 8 Q-registers so we can directly access
2878 // mapped registers S0-S31.
2879 Emit(kArmF32x4Sqrt, g.DefineAsFixed(node, q0),
2880 g.UseFixed(node->InputAt(0), q0));
2881 }
2882
VisitF32x4Div(Node * node)2883 void InstructionSelector::VisitF32x4Div(Node* node) {
2884 ArmOperandGenerator g(this);
2885 // Use fixed registers in the lower 8 Q-registers so we can directly access
2886 // mapped registers S0-S31.
2887 Emit(kArmF32x4Div, g.DefineAsFixed(node, q0),
2888 g.UseFixed(node->InputAt(0), q0), g.UseFixed(node->InputAt(1), q1));
2889 }
2890
VisitS128Select(Node * node)2891 void InstructionSelector::VisitS128Select(Node* node) {
2892 ArmOperandGenerator g(this);
2893 Emit(kArmS128Select, g.DefineSameAsFirst(node),
2894 g.UseRegister(node->InputAt(0)), g.UseRegister(node->InputAt(1)),
2895 g.UseRegister(node->InputAt(2)));
2896 }
2897
VisitI8x16RelaxedLaneSelect(Node * node)2898 void InstructionSelector::VisitI8x16RelaxedLaneSelect(Node* node) {
2899 VisitS128Select(node);
2900 }
2901
VisitI16x8RelaxedLaneSelect(Node * node)2902 void InstructionSelector::VisitI16x8RelaxedLaneSelect(Node* node) {
2903 VisitS128Select(node);
2904 }
2905
VisitI32x4RelaxedLaneSelect(Node * node)2906 void InstructionSelector::VisitI32x4RelaxedLaneSelect(Node* node) {
2907 VisitS128Select(node);
2908 }
2909
VisitI64x2RelaxedLaneSelect(Node * node)2910 void InstructionSelector::VisitI64x2RelaxedLaneSelect(Node* node) {
2911 VisitS128Select(node);
2912 }
2913
2914 #if V8_ENABLE_WEBASSEMBLY
2915 namespace {
2916
2917 struct ShuffleEntry {
2918 uint8_t shuffle[kSimd128Size];
2919 ArchOpcode opcode;
2920 };
2921
2922 static const ShuffleEntry arch_shuffles[] = {
2923 {{0, 1, 2, 3, 16, 17, 18, 19, 4, 5, 6, 7, 20, 21, 22, 23},
2924 kArmS32x4ZipLeft},
2925 {{8, 9, 10, 11, 24, 25, 26, 27, 12, 13, 14, 15, 28, 29, 30, 31},
2926 kArmS32x4ZipRight},
2927 {{0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27},
2928 kArmS32x4UnzipLeft},
2929 {{4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23, 28, 29, 30, 31},
2930 kArmS32x4UnzipRight},
2931 {{0, 1, 2, 3, 16, 17, 18, 19, 8, 9, 10, 11, 24, 25, 26, 27},
2932 kArmS32x4TransposeLeft},
2933 {{4, 5, 6, 7, 20, 21, 22, 23, 12, 13, 14, 15, 28, 29, 30, 31},
2934 kArmS32x4TransposeRight},
2935 {{4, 5, 6, 7, 0, 1, 2, 3, 12, 13, 14, 15, 8, 9, 10, 11}, kArmS32x2Reverse},
2936
2937 {{0, 1, 16, 17, 2, 3, 18, 19, 4, 5, 20, 21, 6, 7, 22, 23},
2938 kArmS16x8ZipLeft},
2939 {{8, 9, 24, 25, 10, 11, 26, 27, 12, 13, 28, 29, 14, 15, 30, 31},
2940 kArmS16x8ZipRight},
2941 {{0, 1, 4, 5, 8, 9, 12, 13, 16, 17, 20, 21, 24, 25, 28, 29},
2942 kArmS16x8UnzipLeft},
2943 {{2, 3, 6, 7, 10, 11, 14, 15, 18, 19, 22, 23, 26, 27, 30, 31},
2944 kArmS16x8UnzipRight},
2945 {{0, 1, 16, 17, 4, 5, 20, 21, 8, 9, 24, 25, 12, 13, 28, 29},
2946 kArmS16x8TransposeLeft},
2947 {{2, 3, 18, 19, 6, 7, 22, 23, 10, 11, 26, 27, 14, 15, 30, 31},
2948 kArmS16x8TransposeRight},
2949 {{6, 7, 4, 5, 2, 3, 0, 1, 14, 15, 12, 13, 10, 11, 8, 9}, kArmS16x4Reverse},
2950 {{2, 3, 0, 1, 6, 7, 4, 5, 10, 11, 8, 9, 14, 15, 12, 13}, kArmS16x2Reverse},
2951
2952 {{0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23},
2953 kArmS8x16ZipLeft},
2954 {{8, 24, 9, 25, 10, 26, 11, 27, 12, 28, 13, 29, 14, 30, 15, 31},
2955 kArmS8x16ZipRight},
2956 {{0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30},
2957 kArmS8x16UnzipLeft},
2958 {{1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31},
2959 kArmS8x16UnzipRight},
2960 {{0, 16, 2, 18, 4, 20, 6, 22, 8, 24, 10, 26, 12, 28, 14, 30},
2961 kArmS8x16TransposeLeft},
2962 {{1, 17, 3, 19, 5, 21, 7, 23, 9, 25, 11, 27, 13, 29, 15, 31},
2963 kArmS8x16TransposeRight},
2964 {{7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8}, kArmS8x8Reverse},
2965 {{3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12}, kArmS8x4Reverse},
2966 {{1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14}, kArmS8x2Reverse}};
2967
TryMatchArchShuffle(const uint8_t * shuffle,const ShuffleEntry * table,size_t num_entries,bool is_swizzle,ArchOpcode * opcode)2968 bool TryMatchArchShuffle(const uint8_t* shuffle, const ShuffleEntry* table,
2969 size_t num_entries, bool is_swizzle,
2970 ArchOpcode* opcode) {
2971 uint8_t mask = is_swizzle ? kSimd128Size - 1 : 2 * kSimd128Size - 1;
2972 for (size_t i = 0; i < num_entries; ++i) {
2973 const ShuffleEntry& entry = table[i];
2974 int j = 0;
2975 for (; j < kSimd128Size; ++j) {
2976 if ((entry.shuffle[j] & mask) != (shuffle[j] & mask)) {
2977 break;
2978 }
2979 }
2980 if (j == kSimd128Size) {
2981 *opcode = entry.opcode;
2982 return true;
2983 }
2984 }
2985 return false;
2986 }
2987
ArrangeShuffleTable(ArmOperandGenerator * g,Node * input0,Node * input1,InstructionOperand * src0,InstructionOperand * src1)2988 void ArrangeShuffleTable(ArmOperandGenerator* g, Node* input0, Node* input1,
2989 InstructionOperand* src0, InstructionOperand* src1) {
2990 if (input0 == input1) {
2991 // Unary, any q-register can be the table.
2992 *src0 = *src1 = g->UseRegister(input0);
2993 } else {
2994 // Binary, table registers must be consecutive.
2995 *src0 = g->UseFixed(input0, q0);
2996 *src1 = g->UseFixed(input1, q1);
2997 }
2998 }
2999
3000 } // namespace
3001
VisitI8x16Shuffle(Node * node)3002 void InstructionSelector::VisitI8x16Shuffle(Node* node) {
3003 uint8_t shuffle[kSimd128Size];
3004 bool is_swizzle;
3005 CanonicalizeShuffle(node, shuffle, &is_swizzle);
3006 Node* input0 = node->InputAt(0);
3007 Node* input1 = node->InputAt(1);
3008 uint8_t shuffle32x4[4];
3009 ArmOperandGenerator g(this);
3010 int index = 0;
3011 if (wasm::SimdShuffle::TryMatch32x4Shuffle(shuffle, shuffle32x4)) {
3012 if (wasm::SimdShuffle::TryMatchSplat<4>(shuffle, &index)) {
3013 DCHECK_GT(4, index);
3014 Emit(kArmS128Dup, g.DefineAsRegister(node), g.UseRegister(input0),
3015 g.UseImmediate(Neon32), g.UseImmediate(index % 4));
3016 } else if (wasm::SimdShuffle::TryMatchIdentity(shuffle)) {
3017 EmitIdentity(node);
3018 } else {
3019 // 32x4 shuffles are implemented as s-register moves. To simplify these,
3020 // make sure the destination is distinct from both sources.
3021 InstructionOperand src0 = g.UseUniqueRegister(input0);
3022 InstructionOperand src1 = is_swizzle ? src0 : g.UseUniqueRegister(input1);
3023 Emit(kArmS32x4Shuffle, g.DefineAsRegister(node), src0, src1,
3024 g.UseImmediate(wasm::SimdShuffle::Pack4Lanes(shuffle32x4)));
3025 }
3026 return;
3027 }
3028 if (wasm::SimdShuffle::TryMatchSplat<8>(shuffle, &index)) {
3029 DCHECK_GT(8, index);
3030 Emit(kArmS128Dup, g.DefineAsRegister(node), g.UseRegister(input0),
3031 g.UseImmediate(Neon16), g.UseImmediate(index % 8));
3032 return;
3033 }
3034 if (wasm::SimdShuffle::TryMatchSplat<16>(shuffle, &index)) {
3035 DCHECK_GT(16, index);
3036 Emit(kArmS128Dup, g.DefineAsRegister(node), g.UseRegister(input0),
3037 g.UseImmediate(Neon8), g.UseImmediate(index % 16));
3038 return;
3039 }
3040 ArchOpcode opcode;
3041 if (TryMatchArchShuffle(shuffle, arch_shuffles, arraysize(arch_shuffles),
3042 is_swizzle, &opcode)) {
3043 VisitRRRShuffle(this, opcode, node);
3044 return;
3045 }
3046 uint8_t offset;
3047 if (wasm::SimdShuffle::TryMatchConcat(shuffle, &offset)) {
3048 Emit(kArmS8x16Concat, g.DefineAsRegister(node), g.UseRegister(input0),
3049 g.UseRegister(input1), g.UseImmediate(offset));
3050 return;
3051 }
3052 // Code generator uses vtbl, arrange sources to form a valid lookup table.
3053 InstructionOperand src0, src1;
3054 ArrangeShuffleTable(&g, input0, input1, &src0, &src1);
3055 Emit(kArmI8x16Shuffle, g.DefineAsRegister(node), src0, src1,
3056 g.UseImmediate(wasm::SimdShuffle::Pack4Lanes(shuffle)),
3057 g.UseImmediate(wasm::SimdShuffle::Pack4Lanes(shuffle + 4)),
3058 g.UseImmediate(wasm::SimdShuffle::Pack4Lanes(shuffle + 8)),
3059 g.UseImmediate(wasm::SimdShuffle::Pack4Lanes(shuffle + 12)));
3060 }
3061 #else
VisitI8x16Shuffle(Node * node)3062 void InstructionSelector::VisitI8x16Shuffle(Node* node) { UNREACHABLE(); }
3063 #endif // V8_ENABLE_WEBASSEMBLY
3064
VisitI8x16Swizzle(Node * node)3065 void InstructionSelector::VisitI8x16Swizzle(Node* node) {
3066 ArmOperandGenerator g(this);
3067 // We don't want input 0 (the table) to be the same as output, since we will
3068 // modify output twice (low and high), and need to keep the table the same.
3069 Emit(kArmI8x16Swizzle, g.DefineAsRegister(node),
3070 g.UseUniqueRegister(node->InputAt(0)), g.UseRegister(node->InputAt(1)));
3071 }
3072
VisitSignExtendWord8ToInt32(Node * node)3073 void InstructionSelector::VisitSignExtendWord8ToInt32(Node* node) {
3074 ArmOperandGenerator g(this);
3075 Emit(kArmSxtb, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0)),
3076 g.TempImmediate(0));
3077 }
3078
VisitSignExtendWord16ToInt32(Node * node)3079 void InstructionSelector::VisitSignExtendWord16ToInt32(Node* node) {
3080 ArmOperandGenerator g(this);
3081 Emit(kArmSxth, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0)),
3082 g.TempImmediate(0));
3083 }
3084
VisitInt32AbsWithOverflow(Node * node)3085 void InstructionSelector::VisitInt32AbsWithOverflow(Node* node) {
3086 UNREACHABLE();
3087 }
3088
VisitInt64AbsWithOverflow(Node * node)3089 void InstructionSelector::VisitInt64AbsWithOverflow(Node* node) {
3090 UNREACHABLE();
3091 }
3092
3093 namespace {
3094 template <ArchOpcode opcode>
VisitBitMask(InstructionSelector * selector,Node * node)3095 void VisitBitMask(InstructionSelector* selector, Node* node) {
3096 ArmOperandGenerator g(selector);
3097 InstructionOperand temps[] = {g.TempSimd128Register()};
3098 selector->Emit(opcode, g.DefineAsRegister(node),
3099 g.UseRegister(node->InputAt(0)), arraysize(temps), temps);
3100 }
3101 } // namespace
3102
VisitI8x16BitMask(Node * node)3103 void InstructionSelector::VisitI8x16BitMask(Node* node) {
3104 VisitBitMask<kArmI8x16BitMask>(this, node);
3105 }
3106
VisitI16x8BitMask(Node * node)3107 void InstructionSelector::VisitI16x8BitMask(Node* node) {
3108 VisitBitMask<kArmI16x8BitMask>(this, node);
3109 }
3110
VisitI32x4BitMask(Node * node)3111 void InstructionSelector::VisitI32x4BitMask(Node* node) {
3112 VisitBitMask<kArmI32x4BitMask>(this, node);
3113 }
3114
VisitI64x2BitMask(Node * node)3115 void InstructionSelector::VisitI64x2BitMask(Node* node) {
3116 VisitBitMask<kArmI64x2BitMask>(this, node);
3117 }
3118
3119 namespace {
VisitF32x4PminOrPmax(InstructionSelector * selector,ArchOpcode opcode,Node * node)3120 void VisitF32x4PminOrPmax(InstructionSelector* selector, ArchOpcode opcode,
3121 Node* node) {
3122 ArmOperandGenerator g(selector);
3123 // Need all unique registers because we first compare the two inputs, then we
3124 // need the inputs to remain unchanged for the bitselect later.
3125 selector->Emit(opcode, g.DefineAsRegister(node),
3126 g.UseUniqueRegister(node->InputAt(0)),
3127 g.UseUniqueRegister(node->InputAt(1)));
3128 }
3129
VisitF64x2PminOrPMax(InstructionSelector * selector,ArchOpcode opcode,Node * node)3130 void VisitF64x2PminOrPMax(InstructionSelector* selector, ArchOpcode opcode,
3131 Node* node) {
3132 ArmOperandGenerator g(selector);
3133 selector->Emit(opcode, g.DefineSameAsFirst(node),
3134 g.UseRegister(node->InputAt(0)),
3135 g.UseRegister(node->InputAt(1)));
3136 }
3137 } // namespace
3138
VisitF32x4Pmin(Node * node)3139 void InstructionSelector::VisitF32x4Pmin(Node* node) {
3140 VisitF32x4PminOrPmax(this, kArmF32x4Pmin, node);
3141 }
3142
VisitF32x4Pmax(Node * node)3143 void InstructionSelector::VisitF32x4Pmax(Node* node) {
3144 VisitF32x4PminOrPmax(this, kArmF32x4Pmax, node);
3145 }
3146
VisitF64x2Pmin(Node * node)3147 void InstructionSelector::VisitF64x2Pmin(Node* node) {
3148 VisitF64x2PminOrPMax(this, kArmF64x2Pmin, node);
3149 }
3150
VisitF64x2Pmax(Node * node)3151 void InstructionSelector::VisitF64x2Pmax(Node* node) {
3152 VisitF64x2PminOrPMax(this, kArmF64x2Pmax, node);
3153 }
3154
VisitF64x2RelaxedMin(Node * node)3155 void InstructionSelector::VisitF64x2RelaxedMin(Node* node) {
3156 VisitF64x2Pmin(node);
3157 }
3158
VisitF64x2RelaxedMax(Node * node)3159 void InstructionSelector::VisitF64x2RelaxedMax(Node* node) {
3160 VisitF64x2Pmax(node);
3161 }
3162
3163 #define EXT_MUL_LIST(V) \
3164 V(I16x8ExtMulLowI8x16S, kArmVmullLow, NeonS8) \
3165 V(I16x8ExtMulHighI8x16S, kArmVmullHigh, NeonS8) \
3166 V(I16x8ExtMulLowI8x16U, kArmVmullLow, NeonU8) \
3167 V(I16x8ExtMulHighI8x16U, kArmVmullHigh, NeonU8) \
3168 V(I32x4ExtMulLowI16x8S, kArmVmullLow, NeonS16) \
3169 V(I32x4ExtMulHighI16x8S, kArmVmullHigh, NeonS16) \
3170 V(I32x4ExtMulLowI16x8U, kArmVmullLow, NeonU16) \
3171 V(I32x4ExtMulHighI16x8U, kArmVmullHigh, NeonU16) \
3172 V(I64x2ExtMulLowI32x4S, kArmVmullLow, NeonS32) \
3173 V(I64x2ExtMulHighI32x4S, kArmVmullHigh, NeonS32) \
3174 V(I64x2ExtMulLowI32x4U, kArmVmullLow, NeonU32) \
3175 V(I64x2ExtMulHighI32x4U, kArmVmullHigh, NeonU32)
3176
3177 #define VISIT_EXT_MUL(OPCODE, VMULL, NEONSIZE) \
3178 void InstructionSelector::Visit##OPCODE(Node* node) { \
3179 VisitRRR(this, VMULL | MiscField::encode(NEONSIZE), node); \
3180 }
3181
3182 EXT_MUL_LIST(VISIT_EXT_MUL)
3183
3184 #undef VISIT_EXT_MUL
3185 #undef EXT_MUL_LIST
3186
3187 #define VISIT_EXTADD_PAIRWISE(OPCODE, NEONSIZE) \
3188 void InstructionSelector::Visit##OPCODE(Node* node) { \
3189 VisitRR(this, kArmVpaddl | MiscField::encode(NEONSIZE), node); \
3190 }
VISIT_EXTADD_PAIRWISE(I16x8ExtAddPairwiseI8x16S,NeonS8)3191 VISIT_EXTADD_PAIRWISE(I16x8ExtAddPairwiseI8x16S, NeonS8)
3192 VISIT_EXTADD_PAIRWISE(I16x8ExtAddPairwiseI8x16U, NeonU8)
3193 VISIT_EXTADD_PAIRWISE(I32x4ExtAddPairwiseI16x8S, NeonS16)
3194 VISIT_EXTADD_PAIRWISE(I32x4ExtAddPairwiseI16x8U, NeonU16)
3195 #undef VISIT_EXTADD_PAIRWISE
3196
3197 void InstructionSelector::VisitTruncateFloat32ToInt32(Node* node) {
3198 ArmOperandGenerator g(this);
3199
3200 InstructionCode opcode = kArmVcvtS32F32;
3201 TruncateKind kind = OpParameter<TruncateKind>(node->op());
3202 if (kind == TruncateKind::kSetOverflowToMin) {
3203 opcode |= MiscField::encode(true);
3204 }
3205
3206 Emit(opcode, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0)));
3207 }
3208
VisitTruncateFloat32ToUint32(Node * node)3209 void InstructionSelector::VisitTruncateFloat32ToUint32(Node* node) {
3210 ArmOperandGenerator g(this);
3211
3212 InstructionCode opcode = kArmVcvtU32F32;
3213 TruncateKind kind = OpParameter<TruncateKind>(node->op());
3214 if (kind == TruncateKind::kSetOverflowToMin) {
3215 opcode |= MiscField::encode(true);
3216 }
3217
3218 Emit(opcode, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0)));
3219 }
3220
3221 // TODO(v8:9780)
3222 // These double precision conversion instructions need a low Q register (q0-q7)
3223 // because the codegen accesses the S registers they overlap with.
VisitF64x2ConvertLowI32x4S(Node * node)3224 void InstructionSelector::VisitF64x2ConvertLowI32x4S(Node* node) {
3225 ArmOperandGenerator g(this);
3226 Emit(kArmF64x2ConvertLowI32x4S, g.DefineAsRegister(node),
3227 g.UseFixed(node->InputAt(0), q0));
3228 }
3229
VisitF64x2ConvertLowI32x4U(Node * node)3230 void InstructionSelector::VisitF64x2ConvertLowI32x4U(Node* node) {
3231 ArmOperandGenerator g(this);
3232 Emit(kArmF64x2ConvertLowI32x4U, g.DefineAsRegister(node),
3233 g.UseFixed(node->InputAt(0), q0));
3234 }
3235
VisitI32x4TruncSatF64x2SZero(Node * node)3236 void InstructionSelector::VisitI32x4TruncSatF64x2SZero(Node* node) {
3237 ArmOperandGenerator g(this);
3238 Emit(kArmI32x4TruncSatF64x2SZero, g.DefineAsFixed(node, q0),
3239 g.UseUniqueRegister(node->InputAt(0)));
3240 }
3241
VisitI32x4TruncSatF64x2UZero(Node * node)3242 void InstructionSelector::VisitI32x4TruncSatF64x2UZero(Node* node) {
3243 ArmOperandGenerator g(this);
3244 Emit(kArmI32x4TruncSatF64x2UZero, g.DefineAsFixed(node, q0),
3245 g.UseUniqueRegister(node->InputAt(0)));
3246 }
3247
VisitF32x4DemoteF64x2Zero(Node * node)3248 void InstructionSelector::VisitF32x4DemoteF64x2Zero(Node* node) {
3249 ArmOperandGenerator g(this);
3250 Emit(kArmF32x4DemoteF64x2Zero, g.DefineAsFixed(node, q0),
3251 g.UseUniqueRegister(node->InputAt(0)));
3252 }
3253
VisitF64x2PromoteLowF32x4(Node * node)3254 void InstructionSelector::VisitF64x2PromoteLowF32x4(Node* node) {
3255 ArmOperandGenerator g(this);
3256 Emit(kArmF64x2PromoteLowF32x4, g.DefineAsRegister(node),
3257 g.UseFixed(node->InputAt(0), q0));
3258 }
3259
VisitI32x4RelaxedTruncF64x2SZero(Node * node)3260 void InstructionSelector::VisitI32x4RelaxedTruncF64x2SZero(Node* node) {
3261 VisitI32x4TruncSatF64x2SZero(node);
3262 }
3263
VisitI32x4RelaxedTruncF64x2UZero(Node * node)3264 void InstructionSelector::VisitI32x4RelaxedTruncF64x2UZero(Node* node) {
3265 VisitI32x4TruncSatF64x2UZero(node);
3266 }
3267
AddOutputToSelectContinuation(OperandGenerator * g,int first_input_index,Node * node)3268 void InstructionSelector::AddOutputToSelectContinuation(OperandGenerator* g,
3269 int first_input_index,
3270 Node* node) {
3271 UNREACHABLE();
3272 }
3273
3274 // static
3275 MachineOperatorBuilder::Flags
SupportedMachineOperatorFlags()3276 InstructionSelector::SupportedMachineOperatorFlags() {
3277 MachineOperatorBuilder::Flags flags = MachineOperatorBuilder::kNoFlags;
3278 if (CpuFeatures::IsSupported(SUDIV)) {
3279 // The sdiv and udiv instructions correctly return 0 if the divisor is 0,
3280 // but the fall-back implementation does not.
3281 flags |= MachineOperatorBuilder::kInt32DivIsSafe |
3282 MachineOperatorBuilder::kUint32DivIsSafe;
3283 }
3284 if (CpuFeatures::IsSupported(ARMv7)) {
3285 flags |= MachineOperatorBuilder::kWord32ReverseBits;
3286 }
3287 if (CpuFeatures::IsSupported(ARMv8)) {
3288 flags |= MachineOperatorBuilder::kFloat32RoundDown |
3289 MachineOperatorBuilder::kFloat64RoundDown |
3290 MachineOperatorBuilder::kFloat32RoundUp |
3291 MachineOperatorBuilder::kFloat64RoundUp |
3292 MachineOperatorBuilder::kFloat32RoundTruncate |
3293 MachineOperatorBuilder::kFloat64RoundTruncate |
3294 MachineOperatorBuilder::kFloat64RoundTiesAway |
3295 MachineOperatorBuilder::kFloat32RoundTiesEven |
3296 MachineOperatorBuilder::kFloat64RoundTiesEven;
3297 }
3298 flags |= MachineOperatorBuilder::kSatConversionIsSafe;
3299 return flags;
3300 }
3301
3302 // static
3303 MachineOperatorBuilder::AlignmentRequirements
AlignmentRequirements()3304 InstructionSelector::AlignmentRequirements() {
3305 base::EnumSet<MachineRepresentation> req_aligned;
3306 req_aligned.Add(MachineRepresentation::kFloat32);
3307 req_aligned.Add(MachineRepresentation::kFloat64);
3308 return MachineOperatorBuilder::AlignmentRequirements::
3309 SomeUnalignedAccessUnsupported(req_aligned, req_aligned);
3310 }
3311
3312 } // namespace compiler
3313 } // namespace internal
3314 } // namespace v8
3315