v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
instruction-selector-riscv.h
Go to the documentation of this file.
1// Copyright 2022 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#ifndef V8_COMPILER_BACKEND_RISCV_INSTRUCTION_SELECTOR_RISCV_H_
6#define V8_COMPILER_BACKEND_RISCV_INSTRUCTION_SELECTOR_RISCV_H_
7
8#include <optional>
9
10#include "src/base/bits.h"
11#include "src/base/logging.h"
13#include "src/common/globals.h"
22#include "src/flags/flags.h"
23
24namespace v8 {
25namespace internal {
26namespace compiler {
27
28#define TRACE(...) PrintF(__VA_ARGS__)
29
30using namespace turboshaft; // NOLINT(build/namespaces)
31
32// Adds RISC-V-specific methods for generating InstructionOperands.
33
35 public:
38
40 if (CanBeImmediate(node, opcode)) {
41 return UseImmediate(node);
42 }
43 return UseRegister(node);
44 }
45
46 // Use the zero register if the node has the immediate value zero, otherwise
47 // assign a register.
49 if (const ConstantOp* constant =
50 selector()->Get(node).TryCast<ConstantOp>()) {
51 if ((constant->IsIntegral() && constant->integral() == 0) ||
52 (constant->kind == ConstantOp::Kind::kFloat32 &&
53 constant->float32().get_bits() == 0) ||
54 (constant->kind == ConstantOp::Kind::kFloat64 &&
55 constant->float64().get_bits() == 0))
56 return UseImmediate(node);
57 }
58 return UseRegister(node);
59 }
60
62 int64_t unused;
63 return selector()->MatchSignedIntegralConstant(node, &unused);
64 }
65
67 return node.has_value() && IsIntegerConstant(node.value());
68 }
69
70 std::optional<int64_t> GetOptionalIntegerConstant(OpIndex operation) {
71 if (int64_t constant; MatchSignedIntegralConstant(operation, &constant)) {
72 return constant;
73 }
74 return std::nullopt;
75 }
76
77 bool CanBeZero(OpIndex node) { return MatchZero(node); }
78
80 const ConstantOp* constant = selector()->Get(node).TryCast<ConstantOp>();
81 if (!constant) return false;
82 if (constant->kind == ConstantOp::Kind::kCompressedHeapObject) {
83 if (!COMPRESS_POINTERS_BOOL) return false;
84 // For builtin code we need static roots
85 if (selector()->isolate()->bootstrapper() && !V8_STATIC_ROOTS_BOOL) {
86 return false;
87 }
88 const RootsTable& roots_table = selector()->isolate()->roots_table();
89 RootIndex root_index;
90 Handle<HeapObject> value = constant->handle();
91 if (roots_table.IsRootHandle(value, &root_index)) {
92 if (!RootsTable::IsReadOnly(root_index)) return false;
94 root_index, selector()->isolate()),
95 mode);
96 }
97 return false;
98 }
99
100 int64_t value;
101 return selector()->MatchSignedIntegralConstant(node, &value) &&
102 CanBeImmediate(value, mode);
103 }
104
105 bool CanBeImmediate(int64_t value, InstructionCode opcode);
106
107 private:
108 bool ImmediateFitsAddrMode1Instruction(int32_t imm) const {
109 TRACE("UNIMPLEMENTED instr_sel: %s at line %d\n", __FUNCTION__, __LINE__);
110 return false;
111 }
112};
113
114void VisitRR(InstructionSelectorT* selector, ArchOpcode opcode, OpIndex node) {
115 RiscvOperandGeneratorT g(selector);
116 selector->Emit(opcode, g.DefineAsRegister(node),
117 g.UseRegister(selector->input_at(node, 0)));
118}
119
121 OpIndex node) {
122 RiscvOperandGeneratorT g(selector);
123 selector->Emit(opcode, g.DefineAsRegister(node),
124 g.UseRegister(selector->input_at(node, 0)));
125}
126
127static void VisitRRI(InstructionSelectorT* selector, ArchOpcode opcode,
128 OpIndex node) {
129 RiscvOperandGeneratorT g(selector);
130 const Operation& op = selector->Get(node);
131 int imm = op.template Cast<Simd128ExtractLaneOp>().lane;
132 selector->Emit(opcode, g.DefineAsRegister(node), g.UseRegister(op.input(0)),
133 g.UseImmediate(imm));
134}
135
136static void VisitSimdShift(InstructionSelectorT* selector, ArchOpcode opcode,
137 OpIndex node) {
138 RiscvOperandGeneratorT g(selector);
139 OpIndex rhs = selector->input_at(node, 1);
140 if (selector->Get(rhs).TryCast<ConstantOp>()) {
141 selector->Emit(opcode, g.DefineAsRegister(node),
142 g.UseRegister(selector->input_at(node, 0)),
143 g.UseImmediate(selector->input_at(node, 1)));
144 } else {
145 selector->Emit(opcode, g.DefineAsRegister(node),
146 g.UseRegister(selector->input_at(node, 0)),
147 g.UseRegister(selector->input_at(node, 1)));
148 }
149}
150
151static void VisitRRIR(InstructionSelectorT* selector, ArchOpcode opcode,
152 OpIndex node) {
153 RiscvOperandGeneratorT g(selector);
154 const turboshaft::Simd128ReplaceLaneOp& op =
155 selector->Get(node).template Cast<turboshaft::Simd128ReplaceLaneOp>();
156 selector->Emit(opcode, g.DefineAsRegister(node), g.UseRegister(op.input(0)),
157 g.UseImmediate(op.lane), g.UseUniqueRegister(op.input(1)));
158}
159
161 OpIndex node,
164 RiscvOperandGeneratorT g(selector);
165 selector->Emit(opcode, g.DefineAsRegister(node),
166 g.UseRegister(selector->input_at(node, 0)),
167 g.UseRegister(selector->input_at(node, 1), kind));
168}
169
170static void VisitUniqueRRR(InstructionSelectorT* selector, ArchOpcode opcode,
171 OpIndex node) {
172 RiscvOperandGeneratorT g(selector);
173 selector->Emit(opcode, g.DefineAsRegister(node),
174 g.UseUniqueRegister(selector->input_at(node, 0)),
175 g.UseUniqueRegister(selector->input_at(node, 1)));
176}
177
178void VisitRRRR(InstructionSelectorT* selector, ArchOpcode opcode,
179 OpIndex node) {
180 RiscvOperandGeneratorT g(selector);
181 selector->Emit(opcode, g.DefineSameAsFirst(node),
182 g.UseRegister(selector->input_at(node, 0)),
183 g.UseRegister(selector->input_at(node, 1)),
184 g.UseRegister(selector->input_at(node, 2)));
185}
186
187static void VisitRRO(InstructionSelectorT* selector, ArchOpcode opcode,
188 OpIndex node) {
189 RiscvOperandGeneratorT g(selector);
190 selector->Emit(opcode, g.DefineAsRegister(node),
191 g.UseRegister(selector->input_at(node, 0)),
192 g.UseOperand(selector->input_at(node, 1), opcode));
193}
194
195bool TryMatchImmediate(InstructionSelectorT* selector,
196 InstructionCode* opcode_return, OpIndex node,
197 size_t* input_count_return, InstructionOperand* inputs) {
198 RiscvOperandGeneratorT g(selector);
199 if (g.CanBeImmediate(node, *opcode_return)) {
200 *opcode_return |= AddressingModeField::encode(kMode_MRI);
201 inputs[0] = g.UseImmediate(node);
202 *input_count_return = 1;
203 return true;
204 }
205 return false;
206}
207
208// Shared routine for multiple binary operations.
209template <typename Matcher>
210static void VisitBinop(InstructionSelectorT* selector, OpIndex node,
211 InstructionCode opcode, bool has_reverse_opcode,
212 InstructionCode reverse_opcode,
213 FlagsContinuationT* cont) {
214 RiscvOperandGeneratorT g(selector);
215 InstructionOperand inputs[2];
216 size_t input_count = 0;
217 InstructionOperand outputs[1];
218 size_t output_count = 0;
219
220 const Operation& binop = selector->Get(node);
221 OpIndex left_node = binop.input(0);
222 OpIndex right_node = binop.input(1);
223
224 if (TryMatchImmediate(selector, &opcode, right_node, &input_count,
225 &inputs[1])) {
226 inputs[0] = g.UseRegisterOrImmediateZero(left_node);
227 input_count++;
228 } else if (has_reverse_opcode &&
229 TryMatchImmediate(selector, &reverse_opcode, left_node,
230 &input_count, &inputs[1])) {
231 inputs[0] = g.UseRegisterOrImmediateZero(right_node);
232 opcode = reverse_opcode;
233 input_count++;
234 } else {
235 inputs[input_count++] = g.UseRegister(left_node);
236 inputs[input_count++] = g.UseOperand(right_node, opcode);
237 }
238
239 if (cont->IsDeoptimize()) {
240 // If we can deoptimize as a result of the binop, we need to make sure that
241 // the deopt inputs are not overwritten by the binop result. One way
242 // to achieve that is to declare the output register as same-as-first.
243 outputs[output_count++] = g.DefineSameAsFirst(node);
244 } else {
245 outputs[output_count++] = g.DefineAsRegister(node);
246 }
247
248 DCHECK_NE(0u, input_count);
249 DCHECK_EQ(1u, output_count);
250 DCHECK_GE(arraysize(inputs), input_count);
251 DCHECK_GE(arraysize(outputs), output_count);
252
253 selector->EmitWithContinuation(opcode, output_count, outputs, input_count,
254 inputs, cont);
255}
256
257template <typename Matcher>
258static void VisitBinop(InstructionSelectorT* selector, OpIndex node,
259 InstructionCode opcode, bool has_reverse_opcode,
260 InstructionCode reverse_opcode) {
262 VisitBinop<Matcher>(selector, node, opcode, has_reverse_opcode,
263 reverse_opcode, &cont);
264}
265
266template <typename Matcher>
267static void VisitBinop(InstructionSelectorT* selector, OpIndex node,
268 InstructionCode opcode, FlagsContinuationT* cont) {
269 VisitBinop<Matcher>(selector, node, opcode, false, kArchNop, cont);
270}
271
272template <typename Matcher>
273static void VisitBinop(InstructionSelectorT* selector, OpIndex node,
274 InstructionCode opcode) {
275 VisitBinop<Matcher>(selector, node, opcode, false, kArchNop);
276}
277
278void InstructionSelectorT::VisitStackSlot(OpIndex node) {
279 const StackSlotOp& stack_slot = Cast<StackSlotOp>(node);
280 int slot = frame_->AllocateSpillSlot(stack_slot.size, stack_slot.alignment,
281 stack_slot.is_tagged);
282 OperandGenerator g(this);
283
284 Emit(kArchStackSlot, g.DefineAsRegister(node),
285 sequence()->AddImmediate(Constant(slot)), 0, nullptr);
286}
287
288void InstructionSelectorT::VisitAbortCSADcheck(OpIndex node) {
289 RiscvOperandGeneratorT g(this);
290 Emit(kArchAbortCSADcheck, g.NoOutput(),
291 g.UseFixed(this->input_at(node, 0), a0));
292}
293
294void EmitS128Load(InstructionSelectorT* selector, OpIndex node,
295 InstructionCode opcode, VSew sew, Vlmul lmul);
296
298 const Simd128LoadTransformOp& op =
299 this->Get(node).Cast<Simd128LoadTransformOp>();
300 bool is_protected = (op.load_kind.with_trap_handler);
301 InstructionCode opcode = kArchNop;
302 switch (op.transform_kind) {
303 case Simd128LoadTransformOp::TransformKind::k8Splat:
304 opcode = kRiscvS128LoadSplat;
305 if (is_protected) {
307 }
308 EmitS128Load(this, node, opcode, E8, m1);
309 break;
310 case Simd128LoadTransformOp::TransformKind::k16Splat:
311 opcode = kRiscvS128LoadSplat;
312 if (is_protected) {
314 }
315 EmitS128Load(this, node, opcode, E16, m1);
316 break;
317 case Simd128LoadTransformOp::TransformKind::k32Splat:
318 opcode = kRiscvS128LoadSplat;
319 if (is_protected) {
321 }
322 EmitS128Load(this, node, opcode, E32, m1);
323 break;
324 case Simd128LoadTransformOp::TransformKind::k64Splat:
325 opcode = kRiscvS128LoadSplat;
326 if (is_protected) {
328 }
329 EmitS128Load(this, node, opcode, E64, m1);
330 break;
331 case Simd128LoadTransformOp::TransformKind::k8x8S:
332 opcode = kRiscvS128Load64ExtendS;
333 if (is_protected) {
335 }
336 EmitS128Load(this, node, opcode, E16, m1);
337 break;
338 case Simd128LoadTransformOp::TransformKind::k8x8U:
339 opcode = kRiscvS128Load64ExtendU;
340 if (is_protected) {
342 }
343 EmitS128Load(this, node, opcode, E16, m1);
344 break;
345 case Simd128LoadTransformOp::TransformKind::k16x4S:
346 opcode = kRiscvS128Load64ExtendS;
347 if (is_protected) {
349 }
350 EmitS128Load(this, node, opcode, E32, m1);
351 break;
352 case Simd128LoadTransformOp::TransformKind::k16x4U:
353 opcode = kRiscvS128Load64ExtendU;
354 if (is_protected) {
356 }
357 EmitS128Load(this, node, opcode, E32, m1);
358 break;
359 case Simd128LoadTransformOp::TransformKind::k32x2S:
360 opcode = kRiscvS128Load64ExtendS;
361 if (is_protected) {
363 }
364 EmitS128Load(this, node, opcode, E64, m1);
365 break;
366 case Simd128LoadTransformOp::TransformKind::k32x2U:
367 opcode = kRiscvS128Load64ExtendU;
368 if (is_protected) {
370 }
371 EmitS128Load(this, node, opcode, E64, m1);
372 break;
373 case Simd128LoadTransformOp::TransformKind::k32Zero:
374 opcode = kRiscvS128Load32Zero;
375 if (is_protected) {
377 }
378 EmitS128Load(this, node, opcode, E32, m1);
379 break;
380 case Simd128LoadTransformOp::TransformKind::k64Zero:
381 opcode = kRiscvS128Load64Zero;
382 if (is_protected) {
384 }
385 EmitS128Load(this, node, opcode, E64, m1);
386 break;
387 default:
389 }
390}
391
392// Shared routine for multiple compare operations.
393
395 InstructionCode opcode,
397 InstructionOperand right,
398 FlagsContinuationT* cont) {
399#ifdef V8_COMPRESS_POINTERS
400 if (opcode == kRiscvCmp32) {
401 RiscvOperandGeneratorT g(selector);
402 InstructionOperand inputs[] = {left, right};
403 if (right.IsImmediate()) {
404 InstructionOperand temps[1] = {g.TempRegister()};
405 return selector->EmitWithContinuation(opcode, 0, nullptr,
406 arraysize(inputs), inputs,
407 arraysize(temps), temps, cont);
408 } else {
409 InstructionOperand temps[2] = {g.TempRegister(), g.TempRegister()};
410 return selector->EmitWithContinuation(opcode, 0, nullptr,
411 arraysize(inputs), inputs,
412 arraysize(temps), temps, cont);
413 }
414 }
415#endif
416 return selector->EmitWithContinuation(opcode, left, right, cont);
417}
418
419// Shared routine for multiple compare operations.
420
422 InstructionOperand value,
423 FlagsContinuationT* cont) {
424 return selector->EmitWithContinuation(kRiscvCmpZero, value, cont);
425}
426
427// Shared routine for multiple float32 compare operations.
428
430 FlagsContinuationT* cont) {
431 RiscvOperandGeneratorT g(selector);
432 const ComparisonOp& op = selector->Get(node).template Cast<ComparisonOp>();
433 OpIndex left = op.left();
434 OpIndex right = op.right();
435 if (selector->MatchZero(right)) {
436 VisitCompare(selector, kRiscvCmpS, g.UseRegister(left),
437 g.UseImmediate(right), cont);
438 } else if (selector->MatchZero(left)) {
439 cont->Commute();
440 VisitCompare(selector, kRiscvCmpS, g.UseRegister(right),
441 g.UseImmediate(left), cont);
442 } else {
443 VisitCompare(selector, kRiscvCmpS, g.UseRegister(left),
444 g.UseRegister(right), cont);
445 }
446}
447
448// Shared routine for multiple float64 compare operations.
449
451 FlagsContinuationT* cont) {
452 RiscvOperandGeneratorT g(selector);
453 const Operation& compare = selector->Get(node);
455 OpIndex lhs = compare.input(0);
456 OpIndex rhs = compare.input(1);
457 if (selector->MatchZero(rhs)) {
458 VisitCompare(selector, kRiscvCmpD, g.UseRegister(lhs), g.UseImmediate(rhs),
459 cont);
460 } else if (selector->MatchZero(lhs)) {
461 VisitCompare(selector, kRiscvCmpD, g.UseImmediate(lhs), g.UseRegister(rhs),
462 cont);
463 } else {
464 VisitCompare(selector, kRiscvCmpD, g.UseRegister(lhs), g.UseRegister(rhs),
465 cont);
466 }
467}
468
469// Shared routine for multiple word compare operations.
470
473 bool commutative) {
474 RiscvOperandGeneratorT g(selector);
475 DCHECK_EQ(selector->value_input_count(node), 2);
476 auto left = selector->input_at(node, 0);
477 auto right = selector->input_at(node, 1);
478 // If one of the two inputs is an immediate, make sure it's on the right.
479 if (!g.CanBeImmediate(right, opcode) && g.CanBeImmediate(left, opcode)) {
480 cont->Commute();
481 std::swap(left, right);
482 }
483 // Match immediates on right side of comparison.
484 if (g.CanBeImmediate(right, opcode)) {
485#if V8_TARGET_ARCH_RISCV64
486 if (opcode == kRiscvTst64 || opcode == kRiscvTst32) {
487#elif V8_TARGET_ARCH_RISCV32
488 if (opcode == kRiscvTst32) {
489#endif
490 return VisitCompare(selector, opcode, g.UseRegister(left),
491 g.UseImmediate(right), cont);
492 } else {
493 switch (cont->condition()) {
494 case kEqual:
495 case kNotEqual:
496 if (cont->IsSet()) {
497 return VisitCompare(selector, opcode, g.UseRegister(left),
498 g.UseImmediate(right), cont);
499 } else {
500 if (g.CanBeZero(right)) {
502 selector, g.UseRegisterOrImmediateZero(left), cont);
503 } else {
504 return VisitCompare(selector, opcode, g.UseRegister(left),
505 g.UseRegister(right), cont);
506 }
507 }
508 break;
509 case kSignedLessThan:
513 if (g.CanBeZero(right)) {
515 selector, g.UseRegisterOrImmediateZero(left), cont);
516 } else {
517 return VisitCompare(selector, opcode, g.UseRegister(left),
518 g.UseImmediate(right), cont);
519 }
520 } break;
521 default:
522 if (g.CanBeZero(right)) {
524 selector, g.UseRegisterOrImmediateZero(left), cont);
525 } else {
526 return VisitCompare(selector, opcode, g.UseRegister(left),
527 g.UseRegister(right), cont);
528 }
529 }
530 }
531 } else {
532 return VisitCompare(selector, opcode, g.UseRegister(left),
533 g.UseRegister(right), cont);
534 }
535}
536
537void InstructionSelectorT::VisitSwitch(OpIndex node, const SwitchInfo& sw) {
539 InstructionOperand value_operand = g.UseRegister(this->input_at(node, 0));
540
541 // Emit either ArchTableSwitch or ArchBinarySearchSwitch.
544 static const size_t kMaxTableSwitchValueRange = 2 << 16;
545 size_t table_space_cost = 10 + 2 * sw.value_range();
546 size_t table_time_cost = 3;
547 size_t lookup_space_cost = 2 + 2 * sw.case_count();
548 size_t lookup_time_cost = sw.case_count();
549 if (sw.case_count() > 0 &&
550 table_space_cost + 3 * table_time_cost <=
551 lookup_space_cost + 3 * lookup_time_cost &&
552 sw.min_value() > std::numeric_limits<int32_t>::min() &&
553 sw.value_range() <= kMaxTableSwitchValueRange) {
554 InstructionOperand index_operand = value_operand;
555 if (sw.min_value()) {
556 index_operand = g.TempRegister();
557 Emit(kRiscvSub32, index_operand, value_operand,
558 g.TempImmediate(sw.min_value()));
559 }
560 // Generate a table lookup.
561 return EmitTableSwitch(sw, index_operand);
562 }
563 }
564
565 // Generate a tree of conditional jumps.
566 return EmitBinarySearchSwitch(sw, value_operand);
567}
568
569void EmitWordCompareZero(InstructionSelectorT* selector, OpIndex value,
570 FlagsContinuationT* cont) {
571 RiscvOperandGeneratorT g(selector);
572 selector->EmitWithContinuation(kRiscvCmpZero,
573 g.UseRegisterOrImmediateZero(value), cont);
574}
575
576#ifdef V8_TARGET_ARCH_RISCV64
577
578void EmitWord32CompareZero(InstructionSelectorT* selector, OpIndex value,
579 FlagsContinuationT* cont) {
580 RiscvOperandGeneratorT g(selector);
581 InstructionOperand inputs[] = {g.UseRegisterOrImmediateZero(value)};
582 InstructionOperand temps[] = {g.TempRegister()};
583 selector->EmitWithContinuation(kRiscvCmpZero32, 0, nullptr, arraysize(inputs),
584 inputs, arraysize(temps), temps, cont);
585}
586#endif
587
588void InstructionSelectorT::VisitFloat32Equal(OpIndex node) {
589 FlagsContinuationT cont = FlagsContinuation::ForSet(kEqual, node);
590 VisitFloat32Compare(this, node, &cont);
591}
592
593void InstructionSelectorT::VisitFloat32LessThan(OpIndex node) {
594 FlagsContinuationT cont = FlagsContinuation::ForSet(kUnsignedLessThan, node);
595 VisitFloat32Compare(this, node, &cont);
596}
597
598void InstructionSelectorT::VisitFloat32LessThanOrEqual(OpIndex node) {
599 FlagsContinuationT cont =
601 VisitFloat32Compare(this, node, &cont);
602}
603
604void InstructionSelectorT::VisitFloat64Equal(OpIndex node) {
605 FlagsContinuationT cont = FlagsContinuation::ForSet(kEqual, node);
606 VisitFloat64Compare(this, node, &cont);
607}
608
609void InstructionSelectorT::VisitFloat64LessThan(OpIndex node) {
610 FlagsContinuationT cont = FlagsContinuation::ForSet(kUnsignedLessThan, node);
611 VisitFloat64Compare(this, node, &cont);
612}
613
614void InstructionSelectorT::VisitFloat64LessThanOrEqual(OpIndex node) {
615 FlagsContinuationT cont =
617 VisitFloat64Compare(this, node, &cont);
618}
619
620void InstructionSelectorT::VisitFloat64ExtractLowWord32(OpIndex node) {
621 VisitRR(this, kRiscvFloat64ExtractLowWord32, node);
622}
623
624void InstructionSelectorT::VisitFloat64ExtractHighWord32(OpIndex node) {
625 VisitRR(this, kRiscvFloat64ExtractHighWord32, node);
626}
627
628void InstructionSelectorT::VisitFloat64SilenceNaN(OpIndex node) {
629 VisitRR(this, kRiscvFloat64SilenceNaN, node);
630}
631
633 RiscvOperandGeneratorT g(this);
634 const auto& bitcast =
636 OpIndex hi = bitcast.high_word32();
637 OpIndex lo = bitcast.low_word32();
638 // TODO(nicohartmann@): We could try to emit a better sequence here.
639 InstructionOperand zero = sequence()->AddImmediate(Constant(0.0));
640 InstructionOperand temp = g.TempDoubleRegister();
641 Emit(kRiscvFloat64InsertHighWord32, temp, zero, g.Use(hi));
642 Emit(kRiscvFloat64InsertLowWord32, g.DefineSameAsFirst(node), temp,
643 g.Use(lo));
644}
645
646void InstructionSelectorT::VisitFloat64InsertLowWord32(OpIndex node) {
647 RiscvOperandGeneratorT g(this);
648 OpIndex left = this->input_at(node, 0);
649 OpIndex right = this->input_at(node, 1);
650 Emit(kRiscvFloat64InsertLowWord32, g.DefineSameAsFirst(node),
651 g.UseRegister(left), g.UseRegister(right));
652}
653
654void InstructionSelectorT::VisitFloat64InsertHighWord32(OpIndex node) {
655 RiscvOperandGeneratorT g(this);
656 OpIndex left = this->input_at(node, 0);
657 OpIndex right = this->input_at(node, 1);
658 Emit(kRiscvFloat64InsertHighWord32, g.DefineSameAsFirst(node),
659 g.UseRegister(left), g.UseRegister(right));
660}
661
662void InstructionSelectorT::VisitMemoryBarrier(OpIndex node) {
663 RiscvOperandGeneratorT g(this);
664 Emit(kRiscvSync, g.NoOutput());
665}
666
668
670 ZoneVector<PushParameter>* results, const CallDescriptor* call_descriptor,
671 OpIndex node) {
672 RiscvOperandGeneratorT g(this);
673
674 for (PushParameter output : *results) {
675 if (!output.location.IsCallerFrameSlot()) continue;
676 // Skip any alignment holes in nodes.
677 if (output.node.valid()) {
678 DCHECK(!call_descriptor->IsCFunctionCall());
679 if (output.location.GetType() == MachineType::Float32()) {
680 MarkAsFloat32(output.node);
681 } else if (output.location.GetType() == MachineType::Float64()) {
682 MarkAsFloat64(output.node);
683 } else if (output.location.GetType() == MachineType::Simd128()) {
684 MarkAsSimd128(output.node);
685 }
686 int offset = call_descriptor->GetOffsetToReturns();
687 int reverse_slot = -output.location.GetLocation() - offset;
688 Emit(kRiscvPeek, g.DefineAsRegister(output.node),
689 g.UseImmediate(reverse_slot));
690 }
691 }
692}
693
695
696void InstructionSelectorT::EmitMoveFPRToParam(InstructionOperand* op,
697 LinkageLocation location) {}
698
699void InstructionSelectorT::VisitFloat32Abs(OpIndex node) {
700 VisitRR(this, kRiscvAbsS, node);
701}
702
703void InstructionSelectorT::VisitFloat64Abs(OpIndex node) {
704 VisitRR(this, kRiscvAbsD, node);
705}
706
707void InstructionSelectorT::VisitFloat32Sqrt(OpIndex node) {
708 VisitRR(this, kRiscvSqrtS, node);
709}
710
711void InstructionSelectorT::VisitFloat64Sqrt(OpIndex node) {
712 VisitRR(this, kRiscvSqrtD, node);
713}
714
715void InstructionSelectorT::VisitFloat32RoundDown(OpIndex node) {
716 VisitRR(this, kRiscvFloat32RoundDown, node);
717}
718
719void InstructionSelectorT::VisitFloat32Add(OpIndex node) {
720 VisitRRR(this, kRiscvAddS, node);
721}
722
723void InstructionSelectorT::VisitFloat64Add(OpIndex node) {
724 VisitRRR(this, kRiscvAddD, node);
725}
726
727void InstructionSelectorT::VisitFloat32Sub(OpIndex node) {
728 VisitRRR(this, kRiscvSubS, node);
729}
730
731void InstructionSelectorT::VisitFloat64Sub(OpIndex node) {
732 VisitRRR(this, kRiscvSubD, node);
733}
734
735void InstructionSelectorT::VisitFloat32Mul(OpIndex node) {
736 VisitRRR(this, kRiscvMulS, node);
737}
738
739void InstructionSelectorT::VisitFloat64Mul(OpIndex node) {
740 VisitRRR(this, kRiscvMulD, node);
741}
742
743void InstructionSelectorT::VisitFloat32Div(OpIndex node) {
744 VisitRRR(this, kRiscvDivS, node);
745}
746
747void InstructionSelectorT::VisitFloat64Div(OpIndex node) {
748 VisitRRR(this, kRiscvDivD, node);
749}
750
751void InstructionSelectorT::VisitFloat64Mod(OpIndex node) {
752 RiscvOperandGeneratorT g(this);
753 Emit(kRiscvModD, g.DefineAsFixed(node, fa0),
754 g.UseFixed(this->input_at(node, 0), fa0),
755 g.UseFixed(this->input_at(node, 1), fa1))
756 ->MarkAsCall();
757}
758
759void InstructionSelectorT::VisitFloat32Max(OpIndex node) {
760 VisitRRR(this, kRiscvFloat32Max, node);
761}
762
763void InstructionSelectorT::VisitFloat64Max(OpIndex node) {
764 VisitRRR(this, kRiscvFloat64Max, node);
765}
766
767void InstructionSelectorT::VisitFloat32Min(OpIndex node) {
768 VisitRRR(this, kRiscvFloat32Min, node);
769}
770
771void InstructionSelectorT::VisitFloat64Min(OpIndex node) {
772 VisitRRR(this, kRiscvFloat64Min, node);
773}
774
775void InstructionSelectorT::VisitTruncateFloat64ToWord32(OpIndex node) {
776 VisitRR(this, kArchTruncateDoubleToI, node);
777}
778
779void InstructionSelectorT::VisitRoundFloat64ToInt32(OpIndex node) {
780 VisitRR(this, kRiscvTruncWD, node);
781}
782
783void InstructionSelectorT::VisitTruncateFloat64ToFloat32(OpIndex node) {
784 RiscvOperandGeneratorT g(this);
785 OpIndex value = this->input_at(node, 0);
786 // Match TruncateFloat64ToFloat32(ChangeInt32ToFloat64) to corresponding
787 // instruction.
788 using Rep = turboshaft::RegisterRepresentation;
789 if (CanCover(node, value)) {
790 const turboshaft::Operation& op = this->Get(value);
791 if (op.Is<turboshaft::ChangeOp>()) {
792 const turboshaft::ChangeOp& change = op.Cast<turboshaft::ChangeOp>();
794 if (change.from == Rep::Word32() && change.to == Rep::Float64()) {
795 Emit(kRiscvCvtSW, g.DefineAsRegister(node),
796 g.UseRegister(this->input_at(value, 0)));
797 return;
798 }
799 }
800 }
801 }
802 VisitRR(this, kRiscvCvtSD, node);
803}
804
805void InstructionSelectorT::VisitWord32Shl(OpIndex node) {
806 // todo(RISCV): Optimize it
807 VisitRRO(this, kRiscvShl32, node);
808}
809
810void InstructionSelectorT::VisitWord32Shr(OpIndex node) {
811 VisitRRO(this, kRiscvShr32, node);
812}
813
814void InstructionSelectorT::VisitWord32Sar(OpIndex node) {
815 // todo(RISCV): Optimize it
816 VisitRRO(this, kRiscvSar32, node);
817}
818
819void InstructionSelectorT::VisitI32x4ExtAddPairwiseI16x8S(OpIndex node) {
820 RiscvOperandGeneratorT g(this);
821 InstructionOperand src1 = g.TempSimd128Register();
822 InstructionOperand src2 = g.TempSimd128Register();
823 InstructionOperand src = g.UseUniqueRegister(this->input_at(node, 0));
824 Emit(kRiscvVrgather, src1, src, g.UseImmediate64(0x0006000400020000),
825 g.UseImmediate(int8_t(E16)), g.UseImmediate(int8_t(m1)));
826 Emit(kRiscvVrgather, src2, src, g.UseImmediate64(0x0007000500030001),
827 g.UseImmediate(int8_t(E16)), g.UseImmediate(int8_t(m1)));
828 Emit(kRiscvVwaddVv, g.DefineAsRegister(node), src1, src2,
829 g.UseImmediate(int8_t(E16)), g.UseImmediate(int8_t(mf2)));
830}
831
832void InstructionSelectorT::VisitI32x4ExtAddPairwiseI16x8U(OpIndex node) {
833 RiscvOperandGeneratorT g(this);
834 InstructionOperand src1 = g.TempSimd128Register();
835 InstructionOperand src2 = g.TempSimd128Register();
836 InstructionOperand src = g.UseUniqueRegister(this->input_at(node, 0));
837 Emit(kRiscvVrgather, src1, src, g.UseImmediate64(0x0006000400020000),
838 g.UseImmediate(int8_t(E16)), g.UseImmediate(int8_t(m1)));
839 Emit(kRiscvVrgather, src2, src, g.UseImmediate64(0x0007000500030001),
840 g.UseImmediate(int8_t(E16)), g.UseImmediate(int8_t(m1)));
841 Emit(kRiscvVwadduVv, g.DefineAsRegister(node), src1, src2,
842 g.UseImmediate(int8_t(E16)), g.UseImmediate(int8_t(mf2)));
843}
844
845void InstructionSelectorT::VisitI16x8ExtAddPairwiseI8x16S(OpIndex node) {
846 RiscvOperandGeneratorT g(this);
847 InstructionOperand src1 = g.TempSimd128Register();
848 InstructionOperand src2 = g.TempSimd128Register();
849 InstructionOperand src = g.UseUniqueRegister(this->input_at(node, 0));
850 Emit(kRiscvVrgather, src1, src, g.UseImmediate64(0x0E0C0A0806040200),
851 g.UseImmediate(int8_t(E8)), g.UseImmediate(int8_t(m1)));
852 Emit(kRiscvVrgather, src2, src, g.UseImmediate64(0x0F0D0B0907050301),
853 g.UseImmediate(int8_t(E8)), g.UseImmediate(int8_t(m1)));
854 Emit(kRiscvVwaddVv, g.DefineAsRegister(node), src1, src2,
855 g.UseImmediate(int8_t(E8)), g.UseImmediate(int8_t(mf2)));
856}
857
858void InstructionSelectorT::VisitI16x8ExtAddPairwiseI8x16U(OpIndex node) {
859 RiscvOperandGeneratorT g(this);
860 InstructionOperand src1 = g.TempSimd128Register();
861 InstructionOperand src2 = g.TempSimd128Register();
862 InstructionOperand src = g.UseUniqueRegister(this->input_at(node, 0));
863 Emit(kRiscvVrgather, src1, src, g.UseImmediate64(0x0E0C0A0806040200),
864 g.UseImmediate(int8_t(E8)), g.UseImmediate(int8_t(m1)));
865 Emit(kRiscvVrgather, src2, src, g.UseImmediate64(0x0F0D0B0907050301),
866 g.UseImmediate(int8_t(E8)), g.UseImmediate(int8_t(m1)));
867 Emit(kRiscvVwadduVv, g.DefineAsRegister(node), src1, src2,
868 g.UseImmediate(int8_t(E8)), g.UseImmediate(int8_t(mf2)));
869}
870
871#define SIMD_INT_TYPE_LIST(V) \
872 V(I64x2, E64, m1) \
873 V(I32x4, E32, m1) \
874 V(I16x8, E16, m1) \
875 V(I8x16, E8, m1)
876
877#define SIMD_TYPE_LIST(V) \
878 V(F32x4) \
879 V(I64x2) \
880 V(I32x4) \
881 V(I16x8) \
882 V(I8x16)
883
884#define SIMD_UNOP_LIST2(V) \
885 V(F32x4Splat, kRiscvVfmvVf, E32, m1) \
886 V(I8x16Neg, kRiscvVnegVv, E8, m1) \
887 V(I16x8Neg, kRiscvVnegVv, E16, m1) \
888 V(I32x4Neg, kRiscvVnegVv, E32, m1) \
889 V(I64x2Neg, kRiscvVnegVv, E64, m1) \
890 V(I8x16Splat, kRiscvVmv, E8, m1) \
891 V(I16x8Splat, kRiscvVmv, E16, m1) \
892 V(I32x4Splat, kRiscvVmv, E32, m1) \
893 V(I64x2Splat, kRiscvVmv, E64, m1) \
894 V(F32x4Neg, kRiscvVfnegVv, E32, m1) \
895 V(F64x2Neg, kRiscvVfnegVv, E64, m1) \
896 V(F64x2Splat, kRiscvVfmvVf, E64, m1) \
897 V(I32x4AllTrue, kRiscvVAllTrue, E32, m1) \
898 V(I16x8AllTrue, kRiscvVAllTrue, E16, m1) \
899 V(I8x16AllTrue, kRiscvVAllTrue, E8, m1) \
900 V(I64x2AllTrue, kRiscvVAllTrue, E64, m1) \
901 V(I64x2Abs, kRiscvVAbs, E64, m1) \
902 V(I32x4Abs, kRiscvVAbs, E32, m1) \
903 V(I16x8Abs, kRiscvVAbs, E16, m1) \
904 V(I8x16Abs, kRiscvVAbs, E8, m1)
905
906#define SIMD_UNOP_LIST(V) \
907 V(F64x2Abs, kRiscvF64x2Abs) \
908 V(F64x2Sqrt, kRiscvF64x2Sqrt) \
909 V(F64x2ConvertLowI32x4S, kRiscvF64x2ConvertLowI32x4S) \
910 V(F64x2ConvertLowI32x4U, kRiscvF64x2ConvertLowI32x4U) \
911 V(F64x2PromoteLowF32x4, kRiscvF64x2PromoteLowF32x4) \
912 V(F64x2Ceil, kRiscvF64x2Ceil) \
913 V(F64x2Floor, kRiscvF64x2Floor) \
914 V(F64x2Trunc, kRiscvF64x2Trunc) \
915 V(F64x2NearestInt, kRiscvF64x2NearestInt) \
916 V(F32x4SConvertI32x4, kRiscvF32x4SConvertI32x4) \
917 V(F32x4UConvertI32x4, kRiscvF32x4UConvertI32x4) \
918 V(F32x4Abs, kRiscvF32x4Abs) \
919 V(F32x4Sqrt, kRiscvF32x4Sqrt) \
920 V(F32x4DemoteF64x2Zero, kRiscvF32x4DemoteF64x2Zero) \
921 V(F32x4Ceil, kRiscvF32x4Ceil) \
922 V(F32x4Floor, kRiscvF32x4Floor) \
923 V(F32x4Trunc, kRiscvF32x4Trunc) \
924 V(F32x4NearestInt, kRiscvF32x4NearestInt) \
925 V(I32x4RelaxedTruncF32x4S, kRiscvI32x4SConvertF32x4) \
926 V(I32x4RelaxedTruncF32x4U, kRiscvI32x4UConvertF32x4) \
927 V(I32x4RelaxedTruncF64x2SZero, kRiscvI32x4TruncSatF64x2SZero) \
928 V(I32x4RelaxedTruncF64x2UZero, kRiscvI32x4TruncSatF64x2UZero) \
929 V(I64x2SConvertI32x4Low, kRiscvI64x2SConvertI32x4Low) \
930 V(I64x2SConvertI32x4High, kRiscvI64x2SConvertI32x4High) \
931 V(I64x2UConvertI32x4Low, kRiscvI64x2UConvertI32x4Low) \
932 V(I64x2UConvertI32x4High, kRiscvI64x2UConvertI32x4High) \
933 V(I32x4SConvertF32x4, kRiscvI32x4SConvertF32x4) \
934 V(I32x4UConvertF32x4, kRiscvI32x4UConvertF32x4) \
935 V(I32x4TruncSatF64x2SZero, kRiscvI32x4TruncSatF64x2SZero) \
936 V(I32x4TruncSatF64x2UZero, kRiscvI32x4TruncSatF64x2UZero) \
937 V(I8x16Popcnt, kRiscvI8x16Popcnt) \
938 V(S128Not, kRiscvVnot) \
939 V(V128AnyTrue, kRiscvV128AnyTrue)
940
941#define SIMD_SHIFT_OP_LIST(V) \
942 V(I64x2Shl) \
943 V(I64x2ShrS) \
944 V(I64x2ShrU) \
945 V(I32x4Shl) \
946 V(I32x4ShrS) \
947 V(I32x4ShrU) \
948 V(I16x8Shl) \
949 V(I16x8ShrS) \
950 V(I16x8ShrU) \
951 V(I8x16Shl) \
952 V(I8x16ShrS) \
953 V(I8x16ShrU)
954
955#define SIMD_BINOP_LIST(V) \
956 V(I64x2Add, kRiscvVaddVv, E64, m1) \
957 V(I32x4Add, kRiscvVaddVv, E32, m1) \
958 V(I16x8Add, kRiscvVaddVv, E16, m1) \
959 V(I8x16Add, kRiscvVaddVv, E8, m1) \
960 V(I64x2Sub, kRiscvVsubVv, E64, m1) \
961 V(I32x4Sub, kRiscvVsubVv, E32, m1) \
962 V(I16x8Sub, kRiscvVsubVv, E16, m1) \
963 V(I8x16Sub, kRiscvVsubVv, E8, m1) \
964 V(I32x4MaxU, kRiscvVmaxuVv, E32, m1) \
965 V(I16x8MaxU, kRiscvVmaxuVv, E16, m1) \
966 V(I8x16MaxU, kRiscvVmaxuVv, E8, m1) \
967 V(I32x4MaxS, kRiscvVmax, E32, m1) \
968 V(I16x8MaxS, kRiscvVmax, E16, m1) \
969 V(I8x16MaxS, kRiscvVmax, E8, m1) \
970 V(I32x4MinS, kRiscvVminsVv, E32, m1) \
971 V(I16x8MinS, kRiscvVminsVv, E16, m1) \
972 V(I8x16MinS, kRiscvVminsVv, E8, m1) \
973 V(I32x4MinU, kRiscvVminuVv, E32, m1) \
974 V(I16x8MinU, kRiscvVminuVv, E16, m1) \
975 V(I8x16MinU, kRiscvVminuVv, E8, m1) \
976 V(I64x2Mul, kRiscvVmulVv, E64, m1) \
977 V(I32x4Mul, kRiscvVmulVv, E32, m1) \
978 V(I16x8Mul, kRiscvVmulVv, E16, m1) \
979 V(I64x2GtS, kRiscvVgtsVv, E64, m1) \
980 V(I32x4GtS, kRiscvVgtsVv, E32, m1) \
981 V(I16x8GtS, kRiscvVgtsVv, E16, m1) \
982 V(I8x16GtS, kRiscvVgtsVv, E8, m1) \
983 V(I64x2GeS, kRiscvVgesVv, E64, m1) \
984 V(I32x4GeS, kRiscvVgesVv, E32, m1) \
985 V(I16x8GeS, kRiscvVgesVv, E16, m1) \
986 V(I8x16GeS, kRiscvVgesVv, E8, m1) \
987 V(I32x4GeU, kRiscvVgeuVv, E32, m1) \
988 V(I16x8GeU, kRiscvVgeuVv, E16, m1) \
989 V(I8x16GeU, kRiscvVgeuVv, E8, m1) \
990 V(I32x4GtU, kRiscvVgtuVv, E32, m1) \
991 V(I16x8GtU, kRiscvVgtuVv, E16, m1) \
992 V(I8x16GtU, kRiscvVgtuVv, E8, m1) \
993 V(I64x2Eq, kRiscvVeqVv, E64, m1) \
994 V(I32x4Eq, kRiscvVeqVv, E32, m1) \
995 V(I16x8Eq, kRiscvVeqVv, E16, m1) \
996 V(I8x16Eq, kRiscvVeqVv, E8, m1) \
997 V(I64x2Ne, kRiscvVneVv, E64, m1) \
998 V(I32x4Ne, kRiscvVneVv, E32, m1) \
999 V(I16x8Ne, kRiscvVneVv, E16, m1) \
1000 V(I8x16Ne, kRiscvVneVv, E8, m1) \
1001 V(I16x8AddSatS, kRiscvVaddSatSVv, E16, m1) \
1002 V(I8x16AddSatS, kRiscvVaddSatSVv, E8, m1) \
1003 V(I16x8AddSatU, kRiscvVaddSatUVv, E16, m1) \
1004 V(I8x16AddSatU, kRiscvVaddSatUVv, E8, m1) \
1005 V(I16x8SubSatS, kRiscvVsubSatSVv, E16, m1) \
1006 V(I8x16SubSatS, kRiscvVsubSatSVv, E8, m1) \
1007 V(I16x8SubSatU, kRiscvVsubSatUVv, E16, m1) \
1008 V(I8x16SubSatU, kRiscvVsubSatUVv, E8, m1) \
1009 V(F64x2Add, kRiscvVfaddVv, E64, m1) \
1010 V(F32x4Add, kRiscvVfaddVv, E32, m1) \
1011 V(F64x2Sub, kRiscvVfsubVv, E64, m1) \
1012 V(F32x4Sub, kRiscvVfsubVv, E32, m1) \
1013 V(F64x2Mul, kRiscvVfmulVv, E64, m1) \
1014 V(F32x4Mul, kRiscvVfmulVv, E32, m1) \
1015 V(F64x2Div, kRiscvVfdivVv, E64, m1) \
1016 V(F32x4Div, kRiscvVfdivVv, E32, m1) \
1017 V(S128And, kRiscvVandVv, E8, m1) \
1018 V(S128Or, kRiscvVorVv, E8, m1) \
1019 V(S128Xor, kRiscvVxorVv, E8, m1) \
1020 V(I16x8Q15MulRSatS, kRiscvVsmulVv, E16, m1) \
1021 V(I16x8RelaxedQ15MulRS, kRiscvVsmulVv, E16, m1)
1022
1023#define UNIMPLEMENTED_SIMD_FP16_OP_LIST(V) \
1024 V(F16x8Splat) \
1025 V(F16x8ExtractLane) \
1026 V(F16x8ReplaceLane) \
1027 V(F16x8Abs) \
1028 V(F16x8Neg) \
1029 V(F16x8Sqrt) \
1030 V(F16x8Floor) \
1031 V(F16x8Ceil) \
1032 V(F16x8Trunc) \
1033 V(F16x8NearestInt) \
1034 V(F16x8Add) \
1035 V(F16x8Sub) \
1036 V(F16x8Mul) \
1037 V(F16x8Div) \
1038 V(F16x8Min) \
1039 V(F16x8Max) \
1040 V(F16x8Pmin) \
1041 V(F16x8Pmax) \
1042 V(F16x8Eq) \
1043 V(F16x8Ne) \
1044 V(F16x8Lt) \
1045 V(F16x8Le) \
1046 V(F16x8SConvertI16x8) \
1047 V(F16x8UConvertI16x8) \
1048 V(I16x8SConvertF16x8) \
1049 V(I16x8UConvertF16x8) \
1050 V(F16x8DemoteF32x4Zero) \
1051 V(F16x8DemoteF64x2Zero) \
1052 V(F32x4PromoteLowF16x8) \
1053 V(F16x8Qfma) \
1054 V(F16x8Qfms)
1055
1056#define SIMD_VISIT_UNIMPL_FP16_OP(Name) \
1057 \
1058 void InstructionSelectorT::Visit##Name(OpIndex node) { UNIMPLEMENTED(); }
1060#undef SIMD_VISIT_UNIMPL_FP16_OP
1061#undef UNIMPLEMENTED_SIMD_FP16_OP_LIST
1062
1063void InstructionSelectorT::VisitS128AndNot(OpIndex node) {
1064 RiscvOperandGeneratorT g(this);
1065 InstructionOperand temp1 = g.TempFpRegister(v0);
1066 this->Emit(kRiscvVnotVv, temp1, g.UseRegister(this->input_at(node, 1)),
1067 g.UseImmediate(E8), g.UseImmediate(m1));
1068 this->Emit(kRiscvVandVv, g.DefineAsRegister(node),
1069 g.UseRegister(this->input_at(node, 0)), temp1, g.UseImmediate(E8),
1070 g.UseImmediate(m1));
1071}
1072
1073void InstructionSelectorT::VisitS128Const(OpIndex node) {
1074 RiscvOperandGeneratorT g(this);
1075 static const int kUint32Immediates = kSimd128Size / sizeof(uint32_t);
1076 uint32_t val[kUint32Immediates];
1077 const turboshaft::Simd128ConstantOp& constant =
1078 this->Get(node).template Cast<turboshaft::Simd128ConstantOp>();
1079 memcpy(val, constant.value, kSimd128Size);
1080 // If all bytes are zeros or ones, avoid emitting code for generic constants
1081 bool all_zeros = !(val[0] || val[1] || val[2] || val[3]);
1082 bool all_ones = val[0] == UINT32_MAX && val[1] == UINT32_MAX &&
1083 val[2] == UINT32_MAX && val[3] == UINT32_MAX;
1084 InstructionOperand dst = g.DefineAsRegister(node);
1085 if (all_zeros) {
1086 Emit(kRiscvS128Zero, dst);
1087 } else if (all_ones) {
1088 Emit(kRiscvS128AllOnes, dst);
1089 } else {
1090 Emit(kRiscvS128Const, dst, g.UseImmediate(val[0]), g.UseImmediate(val[1]),
1091 g.UseImmediate(val[2]), g.UseImmediate(val[3]));
1092 }
1093}
1094
1095void InstructionSelectorT::VisitS128Zero(OpIndex node) {
1096 RiscvOperandGeneratorT g(this);
1097 Emit(kRiscvS128Zero, g.DefineAsRegister(node));
1098}
1099
1100#define SIMD_VISIT_EXTRACT_LANE(Type, Sign) \
1101 \
1102 void InstructionSelectorT::Visit##Type##ExtractLane##Sign(OpIndex node) { \
1103 VisitRRI(this, kRiscv##Type##ExtractLane##Sign, node); \
1104 }
1113#undef SIMD_VISIT_EXTRACT_LANE
1114
1115#define SIMD_VISIT_REPLACE_LANE(Type) \
1116 \
1117 void InstructionSelectorT::Visit##Type##ReplaceLane(OpIndex node) { \
1118 VisitRRIR(this, kRiscv##Type##ReplaceLane, node); \
1119 }
1122#undef SIMD_VISIT_REPLACE_LANE
1123
1124#define SIMD_VISIT_UNOP(Name, instruction) \
1125 \
1126 void InstructionSelectorT::Visit##Name(OpIndex node) { \
1127 VisitRR(this, instruction, node); \
1128 }
1130#undef SIMD_VISIT_UNOP
1131
1132#define SIMD_VISIT_SHIFT_OP(Name) \
1133 \
1134 void InstructionSelectorT::Visit##Name(OpIndex node) { \
1135 VisitSimdShift(this, kRiscv##Name, node); \
1136 }
1138#undef SIMD_VISIT_SHIFT_OP
1139
1140#define SIMD_VISIT_BINOP_RVV(Name, instruction, VSEW, LMUL) \
1141 \
1142 void InstructionSelectorT::Visit##Name(OpIndex node) { \
1143 RiscvOperandGeneratorT g(this); \
1144 this->Emit(instruction, g.DefineAsRegister(node), \
1145 g.UseRegister(this->input_at(node, 0)), \
1146 g.UseRegister(this->input_at(node, 1)), g.UseImmediate(VSEW), \
1147 g.UseImmediate(LMUL)); \
1148 }
1150#undef SIMD_VISIT_BINOP_RVV
1151
1152#define SIMD_VISIT_UNOP2(Name, instruction, VSEW, LMUL) \
1153 \
1154 void InstructionSelectorT::Visit##Name(OpIndex node) { \
1155 RiscvOperandGeneratorT g(this); \
1156 this->Emit(instruction, g.DefineAsRegister(node), \
1157 g.UseRegister(this->input_at(node, 0)), g.UseImmediate(VSEW), \
1158 g.UseImmediate(LMUL)); \
1159 }
1161#undef SIMD_VISIT_UNOP2
1162
1163void InstructionSelectorT::VisitS128Select(OpIndex node) {
1164 VisitRRRR(this, kRiscvS128Select, node);
1165}
1166
1167#define SIMD_VISIT_SELECT_LANE(Name) \
1168 \
1169 void InstructionSelectorT::Visit##Name(OpIndex node) { \
1170 VisitRRRR(this, kRiscvS128Select, node); \
1171 }
1172SIMD_VISIT_SELECT_LANE(I8x16RelaxedLaneSelect)
1173SIMD_VISIT_SELECT_LANE(I16x8RelaxedLaneSelect)
1174SIMD_VISIT_SELECT_LANE(I32x4RelaxedLaneSelect)
1175SIMD_VISIT_SELECT_LANE(I64x2RelaxedLaneSelect)
1176#undef SIMD_VISIT_SELECT_LANE
1177
1178#define VISIT_SIMD_QFMOP(Name, instruction) \
1179 \
1180 void InstructionSelectorT::Visit##Name(OpIndex node) { \
1181 VisitRRRR(this, instruction, node); \
1182 }
1183VISIT_SIMD_QFMOP(F64x2Qfma, kRiscvF64x2Qfma)
1184VISIT_SIMD_QFMOP(F64x2Qfms, kRiscvF64x2Qfms)
1185VISIT_SIMD_QFMOP(F32x4Qfma, kRiscvF32x4Qfma)
1186VISIT_SIMD_QFMOP(F32x4Qfms, kRiscvF32x4Qfms)
1187#undef VISIT_SIMD_QFMOP
1188
1189void InstructionSelectorT::VisitF32x4Min(OpIndex node) {
1190 RiscvOperandGeneratorT g(this);
1191 InstructionOperand temp1 = g.TempFpRegister(v0);
1192 InstructionOperand mask_reg = g.TempFpRegister(v0);
1193 InstructionOperand temp2 = g.TempFpRegister(kSimd128ScratchReg);
1194
1195 this->Emit(kRiscvVmfeqVv, temp1, g.UseRegister(this->input_at(node, 0)),
1196 g.UseRegister(this->input_at(node, 0)), g.UseImmediate(E32),
1197 g.UseImmediate(m1));
1198 this->Emit(kRiscvVmfeqVv, temp2, g.UseRegister(this->input_at(node, 1)),
1199 g.UseRegister(this->input_at(node, 1)), g.UseImmediate(E32),
1200 g.UseImmediate(m1));
1201 this->Emit(kRiscvVandVv, mask_reg, temp2, temp1, g.UseImmediate(E32),
1202 g.UseImmediate(m1));
1203
1204 InstructionOperand NaN = g.TempFpRegister(kSimd128ScratchReg);
1205 InstructionOperand result = g.TempFpRegister(kSimd128ScratchReg);
1206 this->Emit(kRiscvVmv, NaN, g.UseImmediate(0x7FC00000), g.UseImmediate(E32),
1207 g.UseImmediate(m1));
1208 this->Emit(kRiscvVfminVv, result, g.UseRegister(this->input_at(node, 1)),
1209 g.UseRegister(this->input_at(node, 0)), g.UseImmediate(E32),
1210 g.UseImmediate(m1), g.UseImmediate(MaskType::Mask));
1211 this->Emit(kRiscvVmv, g.DefineAsRegister(node), result, g.UseImmediate(E32),
1212 g.UseImmediate(m1));
1213}
1214
1215void InstructionSelectorT::VisitF32x4Max(OpIndex node) {
1216 RiscvOperandGeneratorT g(this);
1217 InstructionOperand temp1 = g.TempFpRegister(v0);
1218 InstructionOperand mask_reg = g.TempFpRegister(v0);
1219 InstructionOperand temp2 = g.TempFpRegister(kSimd128ScratchReg);
1220
1221 this->Emit(kRiscvVmfeqVv, temp1, g.UseRegister(this->input_at(node, 0)),
1222 g.UseRegister(this->input_at(node, 0)), g.UseImmediate(E32),
1223 g.UseImmediate(m1));
1224 this->Emit(kRiscvVmfeqVv, temp2, g.UseRegister(this->input_at(node, 1)),
1225 g.UseRegister(this->input_at(node, 1)), g.UseImmediate(E32),
1226 g.UseImmediate(m1));
1227 this->Emit(kRiscvVandVv, mask_reg, temp2, temp1, g.UseImmediate(E32),
1228 g.UseImmediate(m1));
1229
1230 InstructionOperand NaN = g.TempFpRegister(kSimd128ScratchReg);
1231 InstructionOperand result = g.TempFpRegister(kSimd128ScratchReg);
1232 this->Emit(kRiscvVmv, NaN, g.UseImmediate(0x7FC00000), g.UseImmediate(E32),
1233 g.UseImmediate(m1));
1234 this->Emit(kRiscvVfmaxVv, result, g.UseRegister(this->input_at(node, 1)),
1235 g.UseRegister(this->input_at(node, 0)), g.UseImmediate(E32),
1237 this->Emit(kRiscvVmv, g.DefineAsRegister(node), result, g.UseImmediate(E32),
1238 g.UseImmediate(m1));
1239}
1240
1241void InstructionSelectorT::VisitF32x4RelaxedMin(OpIndex node) {
1242 VisitF32x4Min(node);
1243}
1244
1245void InstructionSelectorT::VisitF64x2RelaxedMin(OpIndex node) {
1246 VisitF64x2Min(node);
1247}
1248
1249void InstructionSelectorT::VisitF64x2RelaxedMax(OpIndex node) {
1250 VisitF64x2Max(node);
1251}
1252
1253void InstructionSelectorT::VisitF32x4RelaxedMax(OpIndex node) {
1254 VisitF32x4Max(node);
1255}
1256
1257void InstructionSelectorT::VisitF64x2Eq(OpIndex node) {
1258 RiscvOperandGeneratorT g(this);
1259 InstructionOperand temp1 = g.TempFpRegister(v0);
1260 this->Emit(kRiscvVmfeqVv, temp1, g.UseRegister(this->input_at(node, 1)),
1261 g.UseRegister(this->input_at(node, 0)), g.UseImmediate(E64),
1262 g.UseImmediate(m1));
1263 InstructionOperand temp2 = g.TempFpRegister(kSimd128ScratchReg);
1264 this->Emit(kRiscvVmv, temp2, g.UseImmediate(0), g.UseImmediate(E64),
1265 g.UseImmediate(m1));
1266 this->Emit(kRiscvVmergeVx, g.DefineAsRegister(node), g.UseImmediate(-1),
1267 temp2, g.UseImmediate(E64), g.UseImmediate(m1));
1268}
1269
1270void InstructionSelectorT::VisitF64x2Ne(OpIndex node) {
1271 RiscvOperandGeneratorT g(this);
1272 InstructionOperand temp1 = g.TempFpRegister(v0);
1273 this->Emit(kRiscvVmfneVv, temp1, g.UseRegister(this->input_at(node, 1)),
1274 g.UseRegister(this->input_at(node, 0)), g.UseImmediate(E64),
1275 g.UseImmediate(m1));
1276 InstructionOperand temp2 = g.TempFpRegister(kSimd128ScratchReg);
1277 this->Emit(kRiscvVmv, temp2, g.UseImmediate(0), g.UseImmediate(E64),
1278 g.UseImmediate(m1));
1279 this->Emit(kRiscvVmergeVx, g.DefineAsRegister(node), g.UseImmediate(-1),
1280 temp2, g.UseImmediate(E64), g.UseImmediate(m1));
1281}
1282
1283void InstructionSelectorT::VisitF64x2Lt(OpIndex node) {
1284 RiscvOperandGeneratorT g(this);
1285 InstructionOperand temp1 = g.TempFpRegister(v0);
1286 this->Emit(kRiscvVmfltVv, temp1, g.UseRegister(this->input_at(node, 0)),
1287 g.UseRegister(this->input_at(node, 1)), g.UseImmediate(E64),
1288 g.UseImmediate(m1));
1289 InstructionOperand temp2 = g.TempFpRegister(kSimd128ScratchReg);
1290 this->Emit(kRiscvVmv, temp2, g.UseImmediate(0), g.UseImmediate(E64),
1291 g.UseImmediate(m1));
1292 this->Emit(kRiscvVmergeVx, g.DefineAsRegister(node), g.UseImmediate(-1),
1293 temp2, g.UseImmediate(E64), g.UseImmediate(m1));
1294}
1295
1296void InstructionSelectorT::VisitF64x2Le(OpIndex node) {
1297 RiscvOperandGeneratorT g(this);
1298 InstructionOperand temp1 = g.TempFpRegister(v0);
1299 this->Emit(kRiscvVmfleVv, temp1, g.UseRegister(this->input_at(node, 0)),
1300 g.UseRegister(this->input_at(node, 1)), g.UseImmediate(E64),
1301 g.UseImmediate(m1));
1302 InstructionOperand temp2 = g.TempFpRegister(kSimd128ScratchReg);
1303 this->Emit(kRiscvVmv, temp2, g.UseImmediate(0), g.UseImmediate(E64),
1304 g.UseImmediate(m1));
1305 this->Emit(kRiscvVmergeVx, g.DefineAsRegister(node), g.UseImmediate(-1),
1306 temp2, g.UseImmediate(E64), g.UseImmediate(m1));
1307}
1308
1309void InstructionSelectorT::VisitF32x4Eq(OpIndex node) {
1310 RiscvOperandGeneratorT g(this);
1311 InstructionOperand temp1 = g.TempFpRegister(v0);
1312 this->Emit(kRiscvVmfeqVv, temp1, g.UseRegister(this->input_at(node, 1)),
1313 g.UseRegister(this->input_at(node, 0)), g.UseImmediate(E32),
1314 g.UseImmediate(m1));
1315 InstructionOperand temp2 = g.TempFpRegister(kSimd128ScratchReg);
1316 this->Emit(kRiscvVmv, temp2, g.UseImmediate(0), g.UseImmediate(E32),
1317 g.UseImmediate(m1));
1318 this->Emit(kRiscvVmergeVx, g.DefineAsRegister(node), g.UseImmediate(-1),
1319 temp2, g.UseImmediate(E32), g.UseImmediate(m1));
1320}
1321
1322void InstructionSelectorT::VisitF32x4Ne(OpIndex node) {
1323 RiscvOperandGeneratorT g(this);
1324 InstructionOperand temp1 = g.TempFpRegister(v0);
1325 this->Emit(kRiscvVmfneVv, temp1, g.UseRegister(this->input_at(node, 1)),
1326 g.UseRegister(this->input_at(node, 0)), g.UseImmediate(E32),
1327 g.UseImmediate(m1));
1328 InstructionOperand temp2 = g.TempFpRegister(kSimd128ScratchReg);
1329 this->Emit(kRiscvVmv, temp2, g.UseImmediate(0), g.UseImmediate(E32),
1330 g.UseImmediate(m1));
1331 this->Emit(kRiscvVmergeVx, g.DefineAsRegister(node), g.UseImmediate(-1),
1332 temp2, g.UseImmediate(E32), g.UseImmediate(m1));
1333}
1334
1335void InstructionSelectorT::VisitF32x4Lt(OpIndex node) {
1336 RiscvOperandGeneratorT g(this);
1337 InstructionOperand temp1 = g.TempFpRegister(v0);
1338 this->Emit(kRiscvVmfltVv, temp1, g.UseRegister(this->input_at(node, 0)),
1339 g.UseRegister(this->input_at(node, 1)), g.UseImmediate(E32),
1340 g.UseImmediate(m1));
1341 InstructionOperand temp2 = g.TempFpRegister(kSimd128ScratchReg);
1342 this->Emit(kRiscvVmv, temp2, g.UseImmediate(0), g.UseImmediate(E32),
1343 g.UseImmediate(m1));
1344 this->Emit(kRiscvVmergeVx, g.DefineAsRegister(node), g.UseImmediate(-1),
1345 temp2, g.UseImmediate(E32), g.UseImmediate(m1));
1346}
1347
1348void InstructionSelectorT::VisitF32x4Le(OpIndex node) {
1349 RiscvOperandGeneratorT g(this);
1350 InstructionOperand temp1 = g.TempFpRegister(v0);
1351 this->Emit(kRiscvVmfleVv, temp1, g.UseRegister(this->input_at(node, 0)),
1352 g.UseRegister(this->input_at(node, 1)), g.UseImmediate(E32),
1353 g.UseImmediate(m1));
1354 InstructionOperand temp2 = g.TempFpRegister(kSimd128ScratchReg);
1355 this->Emit(kRiscvVmv, temp2, g.UseImmediate(0), g.UseImmediate(E32),
1356 g.UseImmediate(m1));
1357 this->Emit(kRiscvVmergeVx, g.DefineAsRegister(node), g.UseImmediate(-1),
1358 temp2, g.UseImmediate(E32), g.UseImmediate(m1));
1359}
1360
1361void InstructionSelectorT::VisitI32x4SConvertI16x8Low(OpIndex node) {
1362 RiscvOperandGeneratorT g(this);
1363 InstructionOperand temp = g.TempFpRegister(kSimd128ScratchReg);
1364 this->Emit(kRiscvVmv, temp, g.UseRegister(this->input_at(node, 0)),
1365 g.UseImmediate(E32), g.UseImmediate(m1));
1366 this->Emit(kRiscvVsextVf2, g.DefineAsRegister(node), temp,
1367 g.UseImmediate(E32), g.UseImmediate(m1));
1368}
1369
1370void InstructionSelectorT::VisitI32x4UConvertI16x8Low(OpIndex node) {
1371 RiscvOperandGeneratorT g(this);
1372 InstructionOperand temp = g.TempFpRegister(kSimd128ScratchReg);
1373 this->Emit(kRiscvVmv, temp, g.UseRegister(this->input_at(node, 0)),
1374 g.UseImmediate(E32), g.UseImmediate(m1));
1375 this->Emit(kRiscvVzextVf2, g.DefineAsRegister(node), temp,
1376 g.UseImmediate(E32), g.UseImmediate(m1));
1377}
1378
1379void InstructionSelectorT::VisitI16x8SConvertI8x16High(OpIndex node) {
1380 RiscvOperandGeneratorT g(this);
1381 InstructionOperand temp1 = g.TempFpRegister(v0);
1382 Emit(kRiscvVslidedown, temp1, g.UseRegister(this->input_at(node, 0)),
1383 g.UseImmediate(8), g.UseImmediate(E8), g.UseImmediate(m1));
1384 Emit(kRiscvVsextVf2, g.DefineAsRegister(node), temp1, g.UseImmediate(E16),
1385 g.UseImmediate(m1));
1386}
1387
1388void InstructionSelectorT::VisitI16x8SConvertI32x4(OpIndex node) {
1389 RiscvOperandGeneratorT g(this);
1390 InstructionOperand temp = g.TempFpRegister(v26);
1391 InstructionOperand temp2 = g.TempFpRegister(v27);
1392 this->Emit(kRiscvVmv, temp, g.UseRegister(this->input_at(node, 0)),
1393 g.UseImmediate(E32), g.UseImmediate(m1));
1394 this->Emit(kRiscvVmv, temp2, g.UseRegister(this->input_at(node, 1)),
1395 g.UseImmediate(E32), g.UseImmediate(m1));
1396 this->Emit(kRiscvVnclip, g.DefineAsRegister(node), temp, g.UseImmediate(0),
1397 g.UseImmediate(E16), g.UseImmediate(m1),
1398 g.UseImmediate(FPURoundingMode::RNE));
1399}
1400
1401void InstructionSelectorT::VisitI16x8UConvertI32x4(OpIndex node) {
1402 RiscvOperandGeneratorT g(this);
1403 InstructionOperand temp = g.TempFpRegister(v26);
1404 InstructionOperand temp2 = g.TempFpRegister(v27);
1405 InstructionOperand temp3 = g.TempFpRegister(v26);
1406 this->Emit(kRiscvVmv, temp, g.UseRegister(this->input_at(node, 0)),
1407 g.UseImmediate(E32), g.UseImmediate(m1));
1408 this->Emit(kRiscvVmv, temp2, g.UseRegister(this->input_at(node, 1)),
1409 g.UseImmediate(E32), g.UseImmediate(m1));
1410 this->Emit(kRiscvVmax, temp3, temp, g.UseImmediate(0), g.UseImmediate(E32),
1411 g.UseImmediate(m2));
1412 this->Emit(kRiscvVnclipu, g.DefineAsRegister(node), temp3, g.UseImmediate(0),
1413 g.UseImmediate(E16), g.UseImmediate(m1),
1414 g.UseImmediate(FPURoundingMode::RNE));
1415}
1416
1417void InstructionSelectorT::VisitI8x16RoundingAverageU(OpIndex node) {
1418 RiscvOperandGeneratorT g(this);
1419 InstructionOperand temp = g.TempFpRegister(kSimd128ScratchReg);
1420 this->Emit(kRiscvVwadduVv, temp, g.UseRegister(this->input_at(node, 0)),
1421 g.UseRegister(this->input_at(node, 1)), g.UseImmediate(E8),
1422 g.UseImmediate(m1));
1423 InstructionOperand temp2 = g.TempFpRegister(kSimd128ScratchReg3);
1424 this->Emit(kRiscvVwadduWx, temp2, temp, g.UseImmediate(1), g.UseImmediate(E8),
1425 g.UseImmediate(m1));
1426 InstructionOperand temp3 = g.TempFpRegister(kSimd128ScratchReg3);
1427 this->Emit(kRiscvVdivu, temp3, temp2, g.UseImmediate(2), g.UseImmediate(E16),
1428 g.UseImmediate(m2));
1429 this->Emit(kRiscvVnclipu, g.DefineAsRegister(node), temp3, g.UseImmediate(0),
1430 g.UseImmediate(E8), g.UseImmediate(m1),
1431 g.UseImmediate(FPURoundingMode::RNE));
1432}
1433
1434void InstructionSelectorT::VisitI8x16SConvertI16x8(OpIndex node) {
1435 RiscvOperandGeneratorT g(this);
1436 InstructionOperand temp = g.TempFpRegister(v26);
1437 InstructionOperand temp2 = g.TempFpRegister(v27);
1438 this->Emit(kRiscvVmv, temp, g.UseRegister(this->input_at(node, 0)),
1439 g.UseImmediate(E16), g.UseImmediate(m1));
1440 this->Emit(kRiscvVmv, temp2, g.UseRegister(this->input_at(node, 1)),
1441 g.UseImmediate(E16), g.UseImmediate(m1));
1442 this->Emit(kRiscvVnclip, g.DefineAsRegister(node), temp, g.UseImmediate(0),
1443 g.UseImmediate(E8), g.UseImmediate(m1),
1444 g.UseImmediate(FPURoundingMode::RNE));
1445}
1446
1447void InstructionSelectorT::VisitI8x16UConvertI16x8(OpIndex node) {
1448 RiscvOperandGeneratorT g(this);
1449 InstructionOperand temp = g.TempFpRegister(v26);
1450 InstructionOperand temp2 = g.TempFpRegister(v27);
1451 InstructionOperand temp3 = g.TempFpRegister(v26);
1452 this->Emit(kRiscvVmv, temp, g.UseRegister(this->input_at(node, 0)),
1453 g.UseImmediate(E16), g.UseImmediate(m1));
1454 this->Emit(kRiscvVmv, temp2, g.UseRegister(this->input_at(node, 1)),
1455 g.UseImmediate(E16), g.UseImmediate(m1));
1456 this->Emit(kRiscvVmax, temp3, temp, g.UseImmediate(0), g.UseImmediate(E16),
1457 g.UseImmediate(m2));
1458 this->Emit(kRiscvVnclipu, g.DefineAsRegister(node), temp3, g.UseImmediate(0),
1459 g.UseImmediate(E8), g.UseImmediate(m1),
1460 g.UseImmediate(FPURoundingMode::RNE));
1461}
1462
1463void InstructionSelectorT::VisitI16x8RoundingAverageU(OpIndex node) {
1464 RiscvOperandGeneratorT g(this);
1465 InstructionOperand temp = g.TempFpRegister(v16);
1466 InstructionOperand temp2 = g.TempFpRegister(v16);
1467 InstructionOperand temp3 = g.TempFpRegister(v16);
1468 this->Emit(kRiscvVwadduVv, temp, g.UseRegister(this->input_at(node, 0)),
1469 g.UseRegister(this->input_at(node, 1)), g.UseImmediate(E16),
1470 g.UseImmediate(m1));
1471 this->Emit(kRiscvVwadduWx, temp2, temp, g.UseImmediate(1),
1472 g.UseImmediate(E16), g.UseImmediate(m1));
1473 this->Emit(kRiscvVdivu, temp3, temp2, g.UseImmediate(2), g.UseImmediate(E32),
1474 g.UseImmediate(m2));
1475 this->Emit(kRiscvVnclipu, g.DefineAsRegister(node), temp3, g.UseImmediate(0),
1476 g.UseImmediate(E16), g.UseImmediate(m1),
1477 g.UseImmediate(FPURoundingMode::RNE));
1478}
1479
1480void InstructionSelectorT::VisitI32x4DotI16x8S(OpIndex node) {
1481 constexpr int32_t FIRST_INDEX = 0b01010101;
1482 constexpr int32_t SECOND_INDEX = 0b10101010;
1483 RiscvOperandGeneratorT g(this);
1484 InstructionOperand temp = g.TempFpRegister(v16);
1485 InstructionOperand temp1 = g.TempFpRegister(v14);
1486 InstructionOperand temp2 = g.TempFpRegister(v30);
1487 InstructionOperand dst = g.DefineAsRegister(node);
1488 this->Emit(kRiscvVwmul, temp, g.UseRegister(this->input_at(node, 0)),
1489 g.UseRegister(this->input_at(node, 1)), g.UseImmediate(E16),
1490 g.UseImmediate(m1));
1491 this->Emit(kRiscvVcompress, temp2, temp, g.UseImmediate(FIRST_INDEX),
1492 g.UseImmediate(E32), g.UseImmediate(m2));
1493 this->Emit(kRiscvVcompress, temp1, temp, g.UseImmediate(SECOND_INDEX),
1494 g.UseImmediate(E32), g.UseImmediate(m2));
1495 this->Emit(kRiscvVaddVv, dst, temp1, temp2, g.UseImmediate(E32),
1496 g.UseImmediate(m1));
1497}
1498
1499void InstructionSelectorT::VisitI16x8DotI8x16I7x16S(OpIndex node) {
1500 constexpr int32_t FIRST_INDEX = 0b0101010101010101;
1501 constexpr int32_t SECOND_INDEX = 0b1010101010101010;
1502 RiscvOperandGeneratorT g(this);
1503 InstructionOperand temp = g.TempFpRegister(v16);
1504 InstructionOperand temp1 = g.TempFpRegister(v14);
1505 InstructionOperand temp2 = g.TempFpRegister(v30);
1506 InstructionOperand dst = g.DefineAsRegister(node);
1507 this->Emit(kRiscvVwmul, temp, g.UseRegister(this->input_at(node, 0)),
1508 g.UseRegister(this->input_at(node, 1)), g.UseImmediate(E8),
1509 g.UseImmediate(m1));
1510 this->Emit(kRiscvVcompress, temp2, temp, g.UseImmediate(FIRST_INDEX),
1511 g.UseImmediate(E16), g.UseImmediate(m2));
1512 this->Emit(kRiscvVcompress, temp1, temp, g.UseImmediate(SECOND_INDEX),
1513 g.UseImmediate(E16), g.UseImmediate(m2));
1514 this->Emit(kRiscvVaddVv, dst, temp1, temp2, g.UseImmediate(E16),
1515 g.UseImmediate(m1));
1516}
1517
1518void InstructionSelectorT::VisitI32x4DotI8x16I7x16AddS(OpIndex node) {
1519 constexpr int32_t FIRST_INDEX = 0b0001000100010001;
1520 constexpr int32_t SECOND_INDEX = 0b0010001000100010;
1521 constexpr int32_t THIRD_INDEX = 0b0100010001000100;
1522 constexpr int32_t FOURTH_INDEX = 0b1000100010001000;
1523 RiscvOperandGeneratorT g(this);
1524 InstructionOperand intermediate = g.TempFpRegister(v12);
1525 this->Emit(kRiscvVwmul, intermediate, g.UseRegister(this->input_at(node, 0)),
1526 g.UseRegister(this->input_at(node, 1)), g.UseImmediate(E8),
1527 g.UseImmediate(m1));
1528
1529 InstructionOperand compressedPart1 = g.TempFpRegister(v14);
1530 InstructionOperand compressedPart2 = g.TempFpRegister(v16);
1531 this->Emit(kRiscvVcompress, compressedPart2, intermediate,
1532 g.UseImmediate(FIRST_INDEX), g.UseImmediate(E16),
1533 g.UseImmediate(m2));
1534 this->Emit(kRiscvVcompress, compressedPart1, intermediate,
1535 g.UseImmediate(SECOND_INDEX), g.UseImmediate(E16),
1536 g.UseImmediate(m2));
1537
1538 InstructionOperand compressedPart3 = g.TempFpRegister(v20);
1539 InstructionOperand compressedPart4 = g.TempFpRegister(v26);
1540 this->Emit(kRiscvVcompress, compressedPart3, intermediate,
1541 g.UseImmediate(THIRD_INDEX), g.UseImmediate(E16),
1542 g.UseImmediate(m2));
1543 this->Emit(kRiscvVcompress, compressedPart4, intermediate,
1544 g.UseImmediate(FOURTH_INDEX), g.UseImmediate(E16),
1545 g.UseImmediate(m2));
1546
1547 InstructionOperand temp2 = g.TempFpRegister(v18);
1548 InstructionOperand temp = g.TempFpRegister(kSimd128ScratchReg);
1549 this->Emit(kRiscvVwaddVv, temp2, compressedPart1, compressedPart2,
1550 g.UseImmediate(E16), g.UseImmediate(m1));
1551 this->Emit(kRiscvVwaddVv, temp, compressedPart3, compressedPart4,
1552 g.UseImmediate(E16), g.UseImmediate(m1));
1553
1554 InstructionOperand mul_result = g.TempFpRegister(v16);
1555 InstructionOperand dst = g.DefineAsRegister(node);
1556 this->Emit(kRiscvVaddVv, mul_result, temp2, temp, g.UseImmediate(E32),
1557 g.UseImmediate(m1));
1558 this->Emit(kRiscvVaddVv, dst, mul_result,
1559 g.UseRegister(this->input_at(node, 2)), g.UseImmediate(E32),
1560 g.UseImmediate(m1));
1561}
1562
1563void InstructionSelectorT::VisitI8x16Shuffle(OpIndex node) {
1564 uint8_t shuffle[kSimd128Size];
1565 bool is_swizzle;
1566 // TODO(riscv): Properly use view here once Turboshaft support is
1567 // implemented.
1568 auto view = this->simd_shuffle_view(node);
1569 CanonicalizeShuffle(view, shuffle, &is_swizzle);
1570 OpIndex input0 = view.input(0);
1571 OpIndex input1 = view.input(1);
1572 RiscvOperandGeneratorT g(this);
1573 // uint8_t shuffle32x4[4];
1574 // ArchOpcode opcode;
1575 // if (TryMatchArchShuffle(shuffle, arch_shuffles, arraysize(arch_shuffles),
1576 // is_swizzle, &opcode)) {
1577 // VisitRRR(this, opcode, node);
1578 // return;
1579 // }
1580 // uint8_t offset;
1581 // if (wasm::SimdShuffle::TryMatchConcat(shuffle, &offset)) {
1582 // Emit(kRiscvS8x16Concat, g.DefineSameAsFirst(node),
1583 // g.UseRegister(input1),
1584 // g.UseRegister(input0), g.UseImmediate(offset));
1585 // return;
1586 // }
1587 // if (wasm::SimdShuffle::TryMatch32x4Shuffle(shuffle, shuffle32x4)) {
1588 // Emit(kRiscvS32x4Shuffle, g.DefineAsRegister(node),
1589 // g.UseRegister(input0),
1590 // g.UseRegister(input1),
1591 // g.UseImmediate(wasm::SimdShuffle::Pack4Lanes(shuffle32x4)));
1592 // return;
1593 // }
1594 Emit(kRiscvI8x16Shuffle, g.DefineAsRegister(node), g.UseRegister(input0),
1595 g.UseRegister(input1),
1600}
1601
1602void InstructionSelectorT::VisitI8x16Swizzle(OpIndex node) {
1603 RiscvOperandGeneratorT g(this);
1604 InstructionOperand temps[] = {g.TempSimd128Register()};
1605 // We don't want input 0 or input 1 to be the same as output, since we will
1606 // modify output before do the calculation.
1607 Emit(kRiscvVrgather, g.DefineAsRegister(node),
1608 g.UseUniqueRegister(this->input_at(node, 0)),
1609 g.UseUniqueRegister(this->input_at(node, 1)), g.UseImmediate(E8),
1610 g.UseImmediate(m1), arraysize(temps), temps);
1611}
1612
1613#define VISIT_BIMASK(TYPE, VSEW, LMUL) \
1614 \
1615 void InstructionSelectorT::Visit##TYPE##BitMask(OpIndex node) { \
1616 RiscvOperandGeneratorT g(this); \
1617 InstructionOperand temp = g.TempFpRegister(v16); \
1618 this->Emit(kRiscvVmslt, temp, g.UseRegister(this->input_at(node, 0)), \
1619 g.UseImmediate(0), g.UseImmediate(VSEW), g.UseImmediate(m1), \
1620 g.UseImmediate(true)); \
1621 this->Emit(kRiscvVmvXs, g.DefineAsRegister(node), temp, \
1622 g.UseImmediate(E32), g.UseImmediate(m1)); \
1623 }
1624
1626#undef VISIT_BIMASK
1627
1628void InstructionSelectorT::VisitI32x4SConvertI16x8High(OpIndex node) {
1629 RiscvOperandGeneratorT g(this);
1630 InstructionOperand temp = g.TempFpRegister(kSimd128ScratchReg);
1631 this->Emit(kRiscvVslidedown, temp, g.UseRegister(this->input_at(node, 0)),
1632 g.UseImmediate(4), g.UseImmediate(E16), g.UseImmediate(m1));
1633 this->Emit(kRiscvVsextVf2, g.DefineAsRegister(node), temp,
1634 g.UseImmediate(E32), g.UseImmediate(m1));
1635}
1636
1637void InstructionSelectorT::VisitI32x4UConvertI16x8High(OpIndex node) {
1638 RiscvOperandGeneratorT g(this);
1639 InstructionOperand temp = g.TempFpRegister(kSimd128ScratchReg);
1640 this->Emit(kRiscvVslidedown, temp, g.UseRegister(this->input_at(node, 0)),
1641 g.UseImmediate(4), g.UseImmediate(E16), g.UseImmediate(m1));
1642 this->Emit(kRiscvVzextVf2, g.DefineAsRegister(node), temp,
1643 g.UseImmediate(E32), g.UseImmediate(m1));
1644}
1645
1646void InstructionSelectorT::VisitI16x8SConvertI8x16Low(OpIndex node) {
1647 RiscvOperandGeneratorT g(this);
1648 InstructionOperand temp = g.TempFpRegister(kSimd128ScratchReg);
1649 this->Emit(kRiscvVmv, temp, g.UseRegister(this->input_at(node, 0)),
1650 g.UseImmediate(E16), g.UseImmediate(m1));
1651 this->Emit(kRiscvVsextVf2, g.DefineAsRegister(node), temp,
1652 g.UseImmediate(E16), g.UseImmediate(m1));
1653}
1654
1655void InstructionSelectorT::VisitI16x8UConvertI8x16High(OpIndex node) {
1656 RiscvOperandGeneratorT g(this);
1657 InstructionOperand temp = g.TempFpRegister(kSimd128ScratchReg);
1658 Emit(kRiscvVslidedown, temp, g.UseRegister(this->input_at(node, 0)),
1659 g.UseImmediate(8), g.UseImmediate(E8), g.UseImmediate(m1));
1660 Emit(kRiscvVzextVf2, g.DefineAsRegister(node), temp, g.UseImmediate(E16),
1661 g.UseImmediate(m1));
1662}
1663
1664void InstructionSelectorT::VisitI16x8UConvertI8x16Low(OpIndex node) {
1665 RiscvOperandGeneratorT g(this);
1666 InstructionOperand temp = g.TempFpRegister(kSimd128ScratchReg);
1667 Emit(kRiscvVmv, temp, g.UseRegister(this->input_at(node, 0)),
1668 g.UseImmediate(E16), g.UseImmediate(m1));
1669 Emit(kRiscvVzextVf2, g.DefineAsRegister(node), temp, g.UseImmediate(E16),
1670 g.UseImmediate(m1));
1671}
1672
1673void InstructionSelectorT::VisitSignExtendWord8ToInt32(OpIndex node) {
1674 RiscvOperandGeneratorT g(this);
1675 Emit(kRiscvSignExtendByte, g.DefineAsRegister(node),
1676 g.UseRegister(this->input_at(node, 0)));
1677}
1678
1679void InstructionSelectorT::VisitSignExtendWord16ToInt32(OpIndex node) {
1680 RiscvOperandGeneratorT g(this);
1681 Emit(kRiscvSignExtendShort, g.DefineAsRegister(node),
1682 g.UseRegister(this->input_at(node, 0)));
1683}
1684
1685void InstructionSelectorT::VisitWord32Clz(OpIndex node) {
1686 VisitRR(this, kRiscvClz32, node);
1687}
1688
1689#define VISIT_EXT_MUL(OPCODE1, OPCODE2, TYPE) \
1690 \
1691 void InstructionSelectorT::Visit##OPCODE1##ExtMulLow##OPCODE2##S( \
1692 OpIndex node) { \
1693 RiscvOperandGeneratorT g(this); \
1694 Emit(kRiscvVwmul, g.DefineAsRegister(node), \
1695 g.UseUniqueRegister(this->input_at(node, 0)), \
1696 g.UseUniqueRegister(this->input_at(node, 1)), \
1697 g.UseImmediate(E##TYPE), g.UseImmediate(mf2)); \
1698 } \
1699 \
1700 void InstructionSelectorT::Visit##OPCODE1##ExtMulHigh##OPCODE2##S( \
1701 OpIndex node) { \
1702 RiscvOperandGeneratorT g(this); \
1703 InstructionOperand t1 = g.TempFpRegister(v16); \
1704 Emit(kRiscvVslidedown, t1, g.UseUniqueRegister(this->input_at(node, 0)), \
1705 g.UseImmediate(kRvvVLEN / TYPE / 2), g.UseImmediate(E##TYPE), \
1706 g.UseImmediate(m1)); \
1707 InstructionOperand t2 = g.TempFpRegister(v17); \
1708 Emit(kRiscvVslidedown, t2, g.UseUniqueRegister(this->input_at(node, 1)), \
1709 g.UseImmediate(kRvvVLEN / TYPE / 2), g.UseImmediate(E##TYPE), \
1710 g.UseImmediate(m1)); \
1711 Emit(kRiscvVwmul, g.DefineAsRegister(node), t1, t2, \
1712 g.UseImmediate(E##TYPE), g.UseImmediate(mf2)); \
1713 } \
1714 \
1715 void InstructionSelectorT::Visit##OPCODE1##ExtMulLow##OPCODE2##U( \
1716 OpIndex node) { \
1717 RiscvOperandGeneratorT g(this); \
1718 Emit(kRiscvVwmulu, g.DefineAsRegister(node), \
1719 g.UseUniqueRegister(this->input_at(node, 0)), \
1720 g.UseUniqueRegister(this->input_at(node, 1)), \
1721 g.UseImmediate(E##TYPE), g.UseImmediate(mf2)); \
1722 } \
1723 \
1724 void InstructionSelectorT::Visit##OPCODE1##ExtMulHigh##OPCODE2##U( \
1725 OpIndex node) { \
1726 RiscvOperandGeneratorT g(this); \
1727 InstructionOperand t1 = g.TempFpRegister(v16); \
1728 Emit(kRiscvVslidedown, t1, g.UseUniqueRegister(this->input_at(node, 0)), \
1729 g.UseImmediate(kRvvVLEN / TYPE / 2), g.UseImmediate(E##TYPE), \
1730 g.UseImmediate(m1)); \
1731 InstructionOperand t2 = g.TempFpRegister(v17); \
1732 Emit(kRiscvVslidedown, t2, g.UseUniqueRegister(this->input_at(node, 1)), \
1733 g.UseImmediate(kRvvVLEN / TYPE / 2), g.UseImmediate(E##TYPE), \
1734 g.UseImmediate(m1)); \
1735 Emit(kRiscvVwmulu, g.DefineAsRegister(node), t1, t2, \
1736 g.UseImmediate(E##TYPE), g.UseImmediate(mf2)); \
1737 }
1738
1739VISIT_EXT_MUL(I64x2, I32x4, 32)
1740VISIT_EXT_MUL(I32x4, I16x8, 16)
1741VISIT_EXT_MUL(I16x8, I8x16, 8)
1742#undef VISIT_EXT_MUL
1743
1744void InstructionSelectorT::VisitF32x4Pmin(OpIndex node) {
1745 VisitUniqueRRR(this, kRiscvF32x4Pmin, node);
1746}
1747
1748void InstructionSelectorT::VisitF32x4Pmax(OpIndex node) {
1749 VisitUniqueRRR(this, kRiscvF32x4Pmax, node);
1750}
1751
1752void InstructionSelectorT::VisitF64x2Pmin(OpIndex node) {
1753 VisitUniqueRRR(this, kRiscvF64x2Pmin, node);
1754}
1755
1756void InstructionSelectorT::VisitF64x2Pmax(OpIndex node) {
1757 VisitUniqueRRR(this, kRiscvF64x2Pmax, node);
1758}
1759
1760void InstructionSelectorT::VisitTruncateFloat64ToFloat16RawBits(OpIndex node) {
1761 UNIMPLEMENTED();
1762}
1763
1764void InstructionSelectorT::VisitChangeFloat16RawBitsToFloat64(OpIndex node) {
1765 UNIMPLEMENTED();
1766}
1767
1768// static
1769MachineOperatorBuilder::AlignmentRequirements
1771#ifdef RISCV_HAS_NO_UNALIGNED
1774#else
1777#endif
1778}
1779
1781 int first_input_index,
1782 OpIndex node) {
1783 UNREACHABLE();
1784}
1785
1786#if V8_ENABLE_WEBASSEMBLY
1787
1788void InstructionSelectorT::VisitSetStackPointer(OpIndex node) {
1789 OperandGenerator g(this);
1790 auto input = g.UseRegister(this->input_at(node, 0));
1791 Emit(kArchSetStackPointer, 0, nullptr, 1, &input);
1792}
1793#endif
1794
1795#undef SIMD_BINOP_LIST
1796#undef SIMD_SHIFT_OP_LIST
1797#undef SIMD_UNOP_LIST
1798#undef SIMD_UNOP_LIST2
1799#undef SIMD_TYPE_LIST
1800#undef SIMD_INT_TYPE_LIST
1801#undef TRACE
1802
1803} // namespace compiler
1804} // namespace internal
1805} // namespace v8
1806
1807#endif // V8_COMPILER_BACKEND_RISCV_INSTRUCTION_SELECTOR_RISCV_H_
Builtins::Kind kind
Definition builtins.cc:40
static constexpr U encode(T value)
Definition bit-field.h:55
RootsTable & roots_table()
Definition isolate.h:1250
static constexpr MachineType Float64()
static constexpr MachineType Simd128()
static constexpr MachineType Float32()
Tagged_t ReadOnlyRootPtr(RootIndex index)
bool IsRootHandle(IndirectHandle< T > handle, RootIndex *index) const
Definition roots-inl.h:65
static constexpr bool IsReadOnly(RootIndex root_index)
Definition roots.h:623
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 AddOutputToSelectContinuation(OperandGenerator *g, int first_input_index, 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 VisitSwitch(turboshaft::OpIndex node, const SwitchInfo &sw)
void EmitMoveParamToFPR(turboshaft::OpIndex node, int index)
void EmitBinarySearchSwitch(const SwitchInfo &sw, InstructionOperand const &value_operand)
void EmitPrepareResults(ZoneVector< PushParameter > *results, const CallDescriptor *call_descriptor, turboshaft::OpIndex node)
static MachineOperatorBuilder::AlignmentRequirements AlignmentRequirements()
ImmediateOperand AddImmediate(const Constant &constant)
InstructionOperand UseFixed(turboshaft::OpIndex node, Register reg)
InstructionOperand DefineAsRegister(turboshaft::OpIndex node)
InstructionOperand Use(turboshaft::OpIndex node)
InstructionOperand DefineSameAsFirst(turboshaft::OpIndex node)
InstructionOperand UseUniqueRegister(turboshaft::OpIndex node)
InstructionOperand UseImmediate64(int64_t immediate)
InstructionOperand UseRegister(turboshaft::OpIndex node)
InstructionOperand TempFpRegister(FPRegType reg)
InstructionOperand DefineAsFixed(turboshaft::OpIndex node, Register reg)
InstructionOperand UseOperand(OpIndex node, InstructionCode opcode)
InstructionOperand UseRegisterOrImmediateZero(OpIndex node)
std::optional< int64_t > GetOptionalIntegerConstant(OpIndex operation)
bool CanBeImmediate(OpIndex node, InstructionCode mode)
const Operation & Get(V< AnyOrNone > op_idx) const
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
static int32_t Pack4Lanes(const uint8_t *shuffle)
#define COMPRESS_POINTERS_BOOL
Definition globals.h:99
Isolate * isolate
#define SIMD_SHIFT_OP_LIST(V)
int32_t offset
#define SIMD_VISIT_SHIFT_OP(Name)
#define SIMD_VISIT_UNIMPL_FP16_OP(Name)
#define VISIT_EXT_MUL(OPCODE1, OPCODE2)
#define SIMD_VISIT_EXTRACT_LANE(Type, Sign)
#define SIMD_VISIT_REPLACE_LANE(Type)
#define SIMD_VISIT_UNOP(Name, instruction)
#define SIMD_TYPE_LIST(V)
#define UNIMPLEMENTED_SIMD_FP16_OP_LIST(V)
#define SIMD_UNOP_LIST2(V)
#define SIMD_VISIT_UNOP2(Name, instruction, VSEW, LMUL)
#define SIMD_VISIT_SELECT_LANE(Name)
#define VISIT_SIMD_QFMOP(Name, instruction)
#define SIMD_INT_TYPE_LIST(V)
#define VISIT_BIMASK(TYPE, VSEW, LMUL)
#define SIMD_VISIT_BINOP_RVV(Name, instruction, VSEW, LMUL)
ZoneVector< RpoNumber > & result
#define TRACE(...)
#define SIMD_UNOP_LIST(V)
#define SIMD_BINOP_LIST(V)
int int32_t
Definition unicode.cc:40
V8_INLINE const Operation & Get(const Graph &graph, OpIndex index)
Definition graph.h:1231
static void VisitRRIR(InstructionSelectorT *selector, ArchOpcode opcode, OpIndex node)
static void VisitRRO(InstructionSelectorT *selector, ArchOpcode opcode, OpIndex node)
void VisitRRRR(InstructionSelectorT *selector, ArchOpcode 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)
void EmitS128Load(InstructionSelectorT *selector, OpIndex node, InstructionCode opcode, VSew sew, Vlmul lmul)
static Instruction * VisitCompare(InstructionSelectorT *selector, InstructionCode opcode, InstructionOperand left, InstructionOperand right, FlagsContinuationT *cont)
static void VisitSimdShift(InstructionSelectorT *selector, ArchOpcode opcode, OpIndex node)
void VisitFloat32Compare(InstructionSelectorT *selector, OpIndex node, FlagsContinuationT *cont)
static void VisitBinop(InstructionSelectorT *selector, turboshaft::OpIndex node, InstructionCode opcode, bool has_reverse_opcode, InstructionCode reverse_opcode, FlagsContinuationT *cont)
Instruction * VisitWordCompare(InstructionSelectorT *selector, OpIndex node, InstructionCode opcode, FlagsContinuationT *cont, bool commutative)
void VisitFloat64Compare(InstructionSelectorT *selector, OpIndex node, FlagsContinuationT *cont)
bool TryMatchImmediate(InstructionSelectorT *selector, InstructionCode *opcode_return, OpIndex node, size_t *input_count_return, InstructionOperand *inputs)
static void VisitUniqueRRR(InstructionSelectorT *selector, ArchOpcode opcode, OpIndex node)
static Instruction * VisitWordCompareZero(InstructionSelectorT *selector, InstructionOperand value, FlagsContinuationT *cont)
constexpr int kSimd128Size
Definition globals.h:706
void EmitWordCompareZero(InstructionSelectorT *selector, OpIndex value, FlagsContinuationT *cont)
constexpr int U
constexpr int S
constexpr Simd128Register kSimd128ScratchReg
constexpr VRegister kSimd128ScratchReg3
return value
Definition map-inl.h:893
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
uint32_t compare
#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 arraysize(array)
Definition macros.h:67
turboshaft::OpIndex input_at(turboshaft::OpIndex node, size_t index) const
turboshaft::Opcode opcode(turboshaft::OpIndex node) const
int value_input_count(turboshaft::OpIndex node) const
V8_INLINE OpIndex input(size_t i) const
Definition operations.h:959
#define V8_STATIC_ROOTS_BOOL
Definition v8config.h:1001