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