v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
operations.cc
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
6
7#include <atomic>
8#include <iomanip>
9#include <optional>
10#include <sstream>
11
12#include "src/base/logging.h"
16#include "src/common/globals.h"
27
28#ifdef DEBUG
29// For InWritableSharedSpace
31#endif
32
33namespace v8::internal {
34std::ostream& operator<<(std::ostream& os, AbortReason reason) {
35 return os << GetAbortReason(reason);
36}
37} // namespace v8::internal
38
40
41void Operation::Print() const { std::cout << *this << "\n"; }
42
43Zone* get_zone(Graph* graph) { return graph->graph_zone(); }
44
45std::optional<Builtin> TryGetBuiltinId(const ConstantOp* target,
47 if (!target) return std::nullopt;
48 if (target->kind != ConstantOp::Kind::kHeapObject) return std::nullopt;
49 // TODO(nicohartmann@): For builtin compilation we don't have a broker. We
50 // could try to access the heap directly instead.
51 if (broker == nullptr) return std::nullopt;
53 AllowHandleDereference allow_handle_dereference;
54 HeapObjectRef ref = MakeRef(broker, target->handle());
55 if (ref.IsCode()) {
56 CodeRef code = ref.AsCode();
57 if (code.object()->is_builtin()) {
58 return code.object()->builtin_id();
59 }
60 }
61 return std::nullopt;
62}
63
65 StackCheckKind kind) const {
66 auto builtin_id =
68 if (!builtin_id.has_value()) return false;
69 if (*builtin_id != Builtin::kCEntry_Return1_ArgvOnStack_NoBuiltinExit) {
70 return false;
71 }
74 auto is_this_builtin = [&](int input_index) {
75 if (const ConstantOp* real_callee =
76 graph.Get(input(input_index)).TryCast<ConstantOp>();
77 real_callee != nullptr &&
78 real_callee->kind == ConstantOp::Kind::kExternal &&
79 real_callee->external_reference() ==
81 return true;
82 }
83 return false;
84 };
85 // The function called by `CEntry_Return1_ArgvOnStack_NoBuiltinExit` is the
86 // 3rd or the 4th argument of the CallOp (depending on the stack check kind),
87 // so we check both of them.
88 return is_this_builtin(2) || is_this_builtin(3);
89}
90
91void CallOp::PrintOptions(std::ostream& os) const {
92 os << '[' << *descriptor->descriptor << ']';
93}
94
95void TailCallOp::PrintOptions(std::ostream& os) const {
96 os << '[' << *descriptor->descriptor << ']';
97}
98
99#if DEBUG
100void ValidateOpInputRep(
101 const Graph& graph, OpIndex input,
102 std::initializer_list<RegisterRepresentation> expected_reps,
103 const Operation* checked_op, std::optional<size_t> projection_index) {
104 const Operation& input_op = graph.Get(input);
106 input_op.outputs_rep();
107 RegisterRepresentation input_rep;
108 if (projection_index) {
109 if (*projection_index < input_reps.size()) {
110 input_rep = input_reps[*projection_index];
111 } else {
112 std::cerr << "Turboshaft operation ";
113 if (checked_op) {
114 std::cerr << *checked_op << " ";
115 }
116 std::cerr << "has input #" << input << ":" << input_op
117 << " with wrong arity.\n";
118 std::cerr << "Input has results " << PrintCollection(input_reps)
119 << ", but expected at least " << (*projection_index + 1)
120 << " results.\n";
121 UNREACHABLE();
122 }
123 } else if (input_reps.size() == 1) {
124 input_rep = input_reps[0];
125 } else {
126 std::cerr << "Turboshaft operation ";
127 if (checked_op) {
128 std::cerr << *checked_op << " ";
129 }
130 std::cerr << "has input #" << input << ":" << input_op
131 << " with wrong arity.\n";
132 std::cerr << "Expected a single output but found " << input_reps.size()
133 << ".\n";
134 UNREACHABLE();
135 }
136 for (RegisterRepresentation expected_rep : expected_reps) {
138 expected_rep, graph.IsCreatedFromTurbofan())) {
139 return;
140 }
141 }
142 std::cerr << "Turboshaft operation ";
143 if (checked_op) {
144 std::cerr << *checked_op << " ";
145 }
146 std::cerr << "has input #" << input << ":" << input_op
147 << " with wrong representation.\n";
148 std::cerr << "Expected " << (expected_reps.size() > 1 ? "one of " : "")
149 << PrintCollection(expected_reps).WithoutBrackets() << " but found "
150 << input_rep << ".\n";
151 std::cout << "Input: " << graph.Get(input) << "\n";
152 UNREACHABLE();
153}
154
155void ValidateOpInputRep(const Graph& graph, OpIndex input,
156 RegisterRepresentation expected_rep,
157 const Operation* checked_op,
158 std::optional<size_t> projection_index) {
159 return ValidateOpInputRep(graph, input, {expected_rep}, checked_op,
160 projection_index);
161}
162#endif // DEBUG
163
164const char* OpcodeName(Opcode opcode) {
165#define OPCODE_NAME(Name) #Name,
166 const char* table[kNumberOfOpcodes] = {
168#undef OPCODE_NAME
169 return table[OpcodeIndex(opcode)];
170}
171
172std::ostream& operator<<(std::ostream& os, OperationPrintStyle styled_op) {
173 const Operation& op = styled_op.op;
174 os << OpcodeName(op.opcode);
175 op.PrintInputs(os, styled_op.op_index_prefix);
176 op.PrintOptions(os);
177 return os;
178}
179
180std::ostream& operator<<(std::ostream& os, RootIndex index) {
181 // Clearly this doesn't maximize convenience, but we don't want to include
182 // all those names in the binary.
183 return os << static_cast<uint16_t>(index);
184}
185
186std::ostream& operator<<(std::ostream& os, GenericBinopOp::Kind kind) {
187 switch (kind) {
188#define PRINT_KIND(Name) \
189 case GenericBinopOp::Kind::k##Name: \
190 return os << #Name;
192#undef PRINT_KIND
193 }
194}
195
196std::ostream& operator<<(std::ostream& os, GenericUnopOp::Kind kind) {
197 switch (kind) {
198#define PRINT_KIND(Name) \
199 case GenericUnopOp::Kind::k##Name: \
200 return os << #Name;
202#undef PRINT_KIND
203 }
204}
205
206std::ostream& operator<<(std::ostream& os, Word32SignHintOp::Sign sign) {
207 switch (sign) {
209 return os << "Signed";
211 return os << "Unsigned";
212 }
213}
214
215std::ostream& operator<<(std::ostream& os, WordUnaryOp::Kind kind) {
216 switch (kind) {
218 return os << "ReverseBytes";
220 return os << "CountLeadingZeros";
222 return os << "CountTrailingZeros";
224 return os << "PopCount";
226 return os << "SignExtend8";
228 return os << "SignExtend16";
229 }
230}
231
232std::ostream& operator<<(std::ostream& os, OverflowCheckedUnaryOp::Kind kind) {
233 switch (kind) {
235 return os << "kAbs";
236 }
237}
238
239std::ostream& operator<<(std::ostream& os, FloatUnaryOp::Kind kind) {
240 switch (kind) {
242 return os << "Abs";
244 return os << "Negate";
246 return os << "SilenceNaN";
248 return os << "RoundUp";
250 return os << "RoundDown";
252 return os << "RoundToZero";
254 return os << "RoundTiesEven";
256 return os << "Log";
258 return os << "Log2";
260 return os << "Log10";
262 return os << "Log1p";
264 return os << "Sqrt";
266 return os << "Cbrt";
268 return os << "Exp";
270 return os << "Expm1";
272 return os << "Sin";
274 return os << "Cos";
276 return os << "Asin";
278 return os << "Acos";
280 return os << "Sinh";
282 return os << "Cosh";
284 return os << "Asinh";
286 return os << "Acosh";
288 return os << "Tan";
290 return os << "Tanh";
292 return os << "Atan";
294 return os << "Atanh";
295 }
296}
297
298// static
300 switch (rep.value()) {
302 switch (kind) {
303 case Kind::kRoundDown:
304 return SupportedOperations::float32_round_down();
305 case Kind::kRoundUp:
306 return SupportedOperations::float32_round_up();
308 return SupportedOperations::float32_round_to_zero();
310 return SupportedOperations::float32_round_ties_even();
311 default:
312 return true;
313 }
315 switch (kind) {
316 case Kind::kRoundDown:
317 return SupportedOperations::float64_round_down();
318 case Kind::kRoundUp:
319 return SupportedOperations::float64_round_up();
321 return SupportedOperations::float64_round_to_zero();
323 return SupportedOperations::float64_round_ties_even();
324 default:
325 return true;
326 }
327 }
328}
329
330// static
332 switch (kind) {
337 return true;
340 ? SupportedOperations::word32_ctz()
341 : SupportedOperations::word64_ctz();
342 case Kind::kPopCount:
344 ? SupportedOperations::word32_popcnt()
345 : SupportedOperations::word64_popcnt();
346 }
347}
348
349std::ostream& operator<<(std::ostream& os, ShiftOp::Kind kind) {
350 switch (kind) {
351 case ShiftOp::Kind::kShiftRightArithmeticShiftOutZeros:
352 return os << "ShiftRightArithmeticShiftOutZeros";
353 case ShiftOp::Kind::kShiftRightArithmetic:
354 return os << "ShiftRightArithmetic";
355 case ShiftOp::Kind::kShiftRightLogical:
356 return os << "ShiftRightLogical";
357 case ShiftOp::Kind::kShiftLeft:
358 return os << "ShiftLeft";
359 case ShiftOp::Kind::kRotateRight:
360 return os << "RotateRight";
361 case ShiftOp::Kind::kRotateLeft:
362 return os << "RotateLeft";
363 }
364}
365
366std::ostream& operator<<(std::ostream& os, ComparisonOp::Kind kind) {
367 switch (kind) {
369 return os << "Equal";
371 return os << "SignedLessThan";
373 return os << "SignedLessThanOrEqual";
375 return os << "UnsignedLessThan";
377 return os << "UnsignedLessThanOrEqual";
378 }
379}
380
381std::ostream& operator<<(std::ostream& os, ChangeOp::Kind kind) {
382 switch (kind) {
384 return os << "FloatConversion";
386 return os << "JSFloatTruncate";
388 return os << "JSFloat16ChangeWithBitcast";
390 return os << "JSFloat16TruncateWithBitcast";
392 return os << "SignedFloatTruncateOverflowToMin";
394 return os << "UnsignedFloatTruncateOverflowToMin";
396 return os << "SignedToFloat";
398 return os << "UnsignedToFloat";
400 return os << "ExtractHighHalf";
402 return os << "ExtractLowHalf";
404 return os << "ZeroExtend";
406 return os << "SignExtend";
408 return os << "Truncate";
410 return os << "Bitcast";
411 }
412}
413
414std::ostream& operator<<(std::ostream& os, ChangeOrDeoptOp::Kind kind) {
415 switch (kind) {
417 return os << "Uint32ToInt32";
419 return os << "Int64ToInt32";
421 return os << "Uint64ToInt32";
423 return os << "Uint64ToInt64";
425 return os << "Float64ToInt32";
427 return os << "Float64ToUint32";
429 return os << "Float64ToAdditiveSafeInteger";
431 return os << "Float64ToInt64";
433 return os << "Float64NotHole";
434 }
435}
436
437std::ostream& operator<<(std::ostream& os, TryChangeOp::Kind kind) {
438 switch (kind) {
440 return os << "SignedFloatTruncateOverflowUndefined";
442 return os << "UnsignedFloatTruncateOverflowUndefined";
443 }
444}
445
446std::ostream& operator<<(std::ostream& os, TaggedBitcastOp::Kind kind) {
447 switch (kind) {
449 return os << "Smi";
451 return os << "HeapObject";
453 return os << "TagAndSmiBits";
455 return os << "Any";
456 }
457}
458
459std::ostream& operator<<(std::ostream& os, ChangeOp::Assumption assumption) {
460 switch (assumption) {
462 return os << "NoAssumption";
464 return os << "NoOverflow";
466 return os << "Reversible";
467 }
468}
469
470std::ostream& operator<<(std::ostream& os, SelectOp::Implementation kind) {
471 switch (kind) {
473 return os << "Branch";
475 return os << "CMove";
476 }
477}
478
479std::ostream& operator<<(std::ostream& os, AtomicRMWOp::BinOp bin_op) {
480 switch (bin_op) {
481 case AtomicRMWOp::BinOp::kAdd:
482 return os << "add";
483 case AtomicRMWOp::BinOp::kSub:
484 return os << "sub";
485 case AtomicRMWOp::BinOp::kAnd:
486 return os << "and";
487 case AtomicRMWOp::BinOp::kOr:
488 return os << "or";
489 case AtomicRMWOp::BinOp::kXor:
490 return os << "xor";
491 case AtomicRMWOp::BinOp::kExchange:
492 return os << "exchange";
493 case AtomicRMWOp::BinOp::kCompareExchange:
494 return os << "compare-exchange";
495 }
496}
497
498std::ostream& operator<<(std::ostream& os, AtomicWord32PairOp::Kind bin_op) {
499 switch (bin_op) {
501 return os << "add";
503 return os << "sub";
505 return os << "and";
507 return os << "or";
509 return os << "xor";
511 return os << "exchange";
513 return os << "compare-exchange";
515 return os << "load";
517 return os << "store";
518 }
519}
520
521std::ostream& operator<<(std::ostream& os, FrameConstantOp::Kind kind) {
522 switch (kind) {
524 return os << "stack check offset";
526 return os << "frame pointer";
528 return os << "parent frame pointer";
529 }
530}
531
532void Operation::PrintInputs(std::ostream& os,
533 const std::string& op_index_prefix) const {
534 switch (opcode) {
535#define SWITCH_CASE(Name) \
536 case Opcode::k##Name: \
537 Cast<Name##Op>().PrintInputs(os, op_index_prefix); \
538 break;
540#undef SWITCH_CASE
541 }
542}
543
544void Operation::PrintOptions(std::ostream& os) const {
545 switch (opcode) {
546#define SWITCH_CASE(Name) \
547 case Opcode::k##Name: \
548 Cast<Name##Op>().PrintOptions(os); \
549 break;
551#undef SWITCH_CASE
552 }
553}
554
555void ConstantOp::PrintOptions(std::ostream& os) const {
556 os << '[';
557 switch (kind) {
558 case Kind::kWord32:
559 os << "word32: " << static_cast<int32_t>(storage.integral);
560 break;
561 case Kind::kWord64:
562 os << "word64: " << static_cast<int64_t>(storage.integral);
563 break;
564 case Kind::kSmi:
565 os << "smi: " << smi();
566 break;
567 case Kind::kNumber:
568 os << "number: " << number().get_scalar();
569 break;
571 os << "tagged index: " << tagged_index();
572 break;
573 case Kind::kFloat64:
574 os << "float64: " << float64().get_scalar();
575 if (float64().is_hole_nan()) {
576 os << " (hole nan: 0x" << std::hex << float64().get_bits() << std::dec
577 << ')';
578 } else if (float64().is_nan()) {
579 os << " (0x" << std::hex << float64().get_bits() << std::dec << ')';
580 }
581 break;
582 case Kind::kFloat32:
583 os << "float32: " << float32().get_scalar();
584 if (float32().is_nan()) {
585 os << " (0x" << std::hex << base::bit_cast<uint32_t>(storage.float32)
586 << std::dec << ')';
587 }
588 break;
589 case Kind::kExternal:
590 os << "external: " << external_reference();
591 break;
593 os << "heap object: " << JSONEscaped(handle());
594 break;
596 os << "compressed heap object: " << JSONEscaped(handle());
597 break;
599 os << "trusted heap object: " << JSONEscaped(handle());
600 break;
602 os << "relocatable wasm call: 0x"
603 << reinterpret_cast<void*>(storage.integral);
604 break;
606 os << "relocatable wasm stub call: 0x"
607 << reinterpret_cast<void*>(storage.integral);
608 break;
610 os << "relocatable wasm canonical signature ID: "
611 << static_cast<int32_t>(storage.integral);
612 break;
614 os << "relocatable wasm indirect call target: "
615 << static_cast<uint32_t>(storage.integral);
616 break;
617 }
618 os << ']';
619}
620
621void ParameterOp::PrintOptions(std::ostream& os) const {
622 os << '[' << parameter_index;
623 if (debug_name) os << ", " << debug_name;
624 os << ']';
625}
626
637
638void LoadOp::PrintInputs(std::ostream& os,
639 const std::string& op_index_prefix) const {
640 os << " *(" << op_index_prefix << base().id();
641 if (offset < 0) {
642 os << " - " << -offset;
643 } else if (offset > 0) {
644 os << " + " << offset;
645 }
646 if (index().valid()) {
647 os << " + " << op_index_prefix << index().value().id();
648 if (element_size_log2 > 0) os << '*' << (1 << element_size_log2);
649 }
650 os << ") ";
651}
652void LoadOp::PrintOptions(std::ostream& os) const {
653 os << '[';
654 os << (kind.tagged_base ? "tagged base" : "raw");
655 if (kind.maybe_unaligned) os << ", unaligned";
656 if (kind.with_trap_handler) os << ", protected";
657 os << ", " << loaded_rep;
658 os << ", " << result_rep;
659 if (element_size_log2 != 0)
660 os << ", element size: 2^" << int{element_size_log2};
661 if (offset != 0) os << ", offset: " << offset;
662 os << ']';
663}
664
665void AtomicRMWOp::PrintInputs(std::ostream& os,
666 const std::string& op_index_prefix) const {
667 os << " *(" << op_index_prefix << base().id() << " + " << op_index_prefix
668 << index().id() << ").atomic_" << bin_op << '(';
669 if (bin_op == BinOp::kCompareExchange) {
670 os << "expected: " << op_index_prefix << expected();
671 os << ", new: " << op_index_prefix << value();
672 } else {
673 os << op_index_prefix << value().id();
674 }
675 os << ')';
676}
677
678void AtomicRMWOp::PrintOptions(std::ostream& os) const {
679 os << '[' << "binop: " << bin_op << ", in_out_rep: " << in_out_rep
680 << ", memory_rep: " << memory_rep << ']';
681}
682
684 const std::string& op_index_prefix) const {
685 os << " *(" << op_index_prefix << base().id();
686 if (index().valid()) {
687 os << " + " << op_index_prefix << index().value().id();
688 }
689 if (offset) {
690 os << " + offset=" << offset;
691 }
692 os << ").atomic_word32_pair_" << kind << '(';
694 os << "expected: {lo: " << op_index_prefix << value_low()
695 << ", hi: " << op_index_prefix << value_high();
696 os << "}, value: {lo: " << op_index_prefix << value_low()
697 << ", hi: " << op_index_prefix << value_high() << '}';
698 } else if (kind != Kind::kLoad) {
699 os << "lo: " << op_index_prefix << value_low()
700 << ", hi: " << op_index_prefix << value_high();
701 }
702 os << ')';
703}
704
705void AtomicWord32PairOp::PrintOptions(std::ostream& os) const {
706 os << "[opkind: " << kind << ']';
707}
708
709void MemoryBarrierOp::PrintOptions(std::ostream& os) const {
710 os << "[memory order: " << memory_order << ']';
711}
712
713void StoreOp::PrintInputs(std::ostream& os,
714 const std::string& op_index_prefix) const {
715 os << " *(" << op_index_prefix << base().id();
716 if (offset < 0) {
717 os << " - " << -offset;
718 } else if (offset > 0) {
719 os << " + " << offset;
720 }
721 if (index().valid()) {
722 os << " + " << op_index_prefix << index().value().id();
723 if (element_size_log2 > 0) os << '*' << (1 << element_size_log2);
724 }
725 os << ") = " << op_index_prefix << value().id() << ' ';
726}
727void StoreOp::PrintOptions(std::ostream& os) const {
728 os << '[';
729 os << (kind.tagged_base ? "tagged base" : "raw");
730 if (kind.maybe_unaligned) os << ", unaligned";
731 if (kind.with_trap_handler) os << ", protected";
732 os << ", " << stored_rep;
733 os << ", " << write_barrier;
734 if (element_size_log2 != 0)
735 os << ", element size: 2^" << int{element_size_log2};
736 if (offset != 0) os << ", offset: " << offset;
737 if (maybe_initializing_or_transitioning) os << ", initializing";
738 os << ']';
739}
740
741void AllocateOp::PrintOptions(std::ostream& os) const {
742 os << '[';
743 os << type;
744 os << ']';
745}
746
747void DecodeExternalPointerOp::PrintOptions(std::ostream& os) const {
748 os << '[';
749 os << "tag_range: [" << tag_range.first << ", " << tag_range.last << "]";
750 os << ']';
751}
752
753void FrameStateOp::PrintOptions(std::ostream& os) const {
754 os << '[';
755 os << (inlined ? "inlined" : "not inlined");
756 os << ", ";
757 os << data->frame_state_info;
758 os << ", state values:";
759 FrameStateData::Iterator it = data->iterator(state_values());
760 while (it.has_more()) {
761 os << ' ';
762 switch (it.current_instr()) {
763 case FrameStateData::Instr::kInput: {
766 it.ConsumeInput(&type, &input);
767 os << '#' << input.id() << '(' << type << ')';
768 break;
769 }
770 case FrameStateData::Instr::kUnusedRegister:
771 it.ConsumeUnusedRegister();
772 os << '.';
773 break;
774 case FrameStateData::Instr::kDematerializedObject: {
775 uint32_t id;
776 uint32_t field_count;
777 it.ConsumeDematerializedObject(&id, &field_count);
778 os << '$' << id << "(field count: " << field_count << ')';
779 break;
780 }
781 case FrameStateData::Instr::kDematerializedObjectReference: {
782 uint32_t id;
783 it.ConsumeDematerializedObjectReference(&id);
784 os << '$' << id;
785 break;
786 }
787 case FrameStateData::Instr::kDematerializedStringConcat: {
788 uint32_t id;
789 it.ConsumeDematerializedStringConcat(&id);
790 os << "£" << id << "DematerializedStringConcat";
791 break;
792 }
793 case FrameStateData::Instr::kDematerializedStringConcatReference: {
794 uint32_t id;
795 it.ConsumeDematerializedStringConcatReference(&id);
796 os << "£" << id;
797 break;
798 }
799 case FrameStateData::Instr::kArgumentsElements: {
801 it.ConsumeArgumentsElements(&type);
802 os << "ArgumentsElements(" << type << ')';
803 break;
804 }
805 case FrameStateData::Instr::kArgumentsLength: {
806 it.ConsumeArgumentsLength();
807 os << "ArgumentsLength";
808 break;
809 }
810 case FrameStateData::Instr::kRestLength: {
811 it.ConsumeRestLength();
812 os << "RestLength";
813 break;
814 }
815 }
816 }
817 os << ']';
818}
819
820void FrameStateOp::Validate(const Graph& graph) const {
821 if (inlined) {
823 }
824 FrameStateData::Iterator it = data->iterator(state_values());
825 while (it.has_more()) {
826 switch (it.current_instr()) {
827 case FrameStateData::Instr::kInput: {
830 it.ConsumeInput(&type, &input);
833 type.representation());
834 if (rep == RegisterRepresentation::Tagged()) {
835 // The deoptimizer can handle compressed values.
837 }
838#ifdef DEBUG
839 ValidateOpInputRep(graph, input, rep);
840#endif // DEBUG
841 break;
842 }
843 case FrameStateData::Instr::kUnusedRegister:
844 it.ConsumeUnusedRegister();
845 break;
846 case FrameStateData::Instr::kDematerializedObject: {
847 uint32_t id;
848 uint32_t field_count;
849 it.ConsumeDematerializedObject(&id, &field_count);
850 break;
851 }
852 case FrameStateData::Instr::kDematerializedObjectReference: {
853 uint32_t id;
854 it.ConsumeDematerializedObjectReference(&id);
855 break;
856 }
857 case FrameStateData::Instr::kDematerializedStringConcat: {
858 uint32_t id;
859 it.ConsumeDematerializedStringConcat(&id);
860 break;
861 }
862 case FrameStateData::Instr::kDematerializedStringConcatReference: {
863 uint32_t id;
864 it.ConsumeDematerializedStringConcatReference(&id);
865 break;
866 }
867 case FrameStateData::Instr::kArgumentsElements: {
869 it.ConsumeArgumentsElements(&type);
870 break;
871 }
872 case FrameStateData::Instr::kArgumentsLength: {
873 it.ConsumeArgumentsLength();
874 break;
875 }
876 case FrameStateData::Instr::kRestLength: {
877 it.ConsumeRestLength();
878 break;
879 }
880 }
881 }
882}
883
884void DeoptimizeIfOp::PrintOptions(std::ostream& os) const {
885 static_assert(std::tuple_size_v<decltype(options())> == 2);
886 os << '[' << (negated ? "negated, " : "") << *parameters << ']';
887}
888
889void DidntThrowOp::Validate(const Graph& graph) const {
890#ifdef DEBUG
892 switch (graph.Get(throwing_operation()).opcode) {
893 case Opcode::kCall: {
894 auto& call_op = graph.Get(throwing_operation()).Cast<CallOp>();
895 DCHECK_EQ(call_op.descriptor->out_reps, outputs_rep());
896 break;
897 }
898 case Opcode::kFastApiCall: {
899 auto& call_op = graph.Get(throwing_operation()).Cast<FastApiCallOp>();
900 DCHECK_EQ(call_op.out_reps, outputs_rep());
901 break;
902 }
903#define STATIC_OUTPUT_CASE(Name) \
904 case Opcode::k##Name: { \
905 const Name##Op& op = graph.Get(throwing_operation()).Cast<Name##Op>(); \
906 DCHECK_EQ(op.kOutReps, outputs_rep()); \
907 break; \
908 }
910#undef STATIC_OUTPUT_CASE
911 default:
912 UNREACHABLE();
913 }
914 // Check that `may_throw()` is either immediately before or that there is only
915 // a `CheckExceptionOp` in-between.
916 OpIndex this_index = graph.Index(*this);
917 OpIndex in_between = graph.NextIndex(throwing_operation());
918 if (has_catch_block) {
919 DCHECK_NE(in_between, this_index);
920 auto& catch_op = graph.Get(in_between).Cast<CheckExceptionOp>();
921 DCHECK_EQ(catch_op.didnt_throw_block->begin(), this_index);
922 } else {
923 DCHECK_EQ(in_between, this_index);
924 }
925#endif
926}
927
928void WordBinopOp::PrintOptions(std::ostream& os) const {
929 os << '[';
930 switch (kind) {
931 case Kind::kAdd:
932 os << "Add, ";
933 break;
934 case Kind::kSub:
935 os << "Sub, ";
936 break;
937 case Kind::kMul:
938 os << "Mul, ";
939 break;
941 os << "SignedMulOverflownBits, ";
942 break;
944 os << "UnsignedMulOverflownBits, ";
945 break;
946 case Kind::kSignedDiv:
947 os << "SignedDiv, ";
948 break;
950 os << "UnsignedDiv, ";
951 break;
952 case Kind::kSignedMod:
953 os << "SignedMod, ";
954 break;
956 os << "UnsignedMod, ";
957 break;
959 os << "BitwiseAnd, ";
960 break;
961 case Kind::kBitwiseOr:
962 os << "BitwiseOr, ";
963 break;
965 os << "BitwiseXor, ";
966 break;
967 }
968 os << rep;
969 os << ']';
970}
971
972void FloatBinopOp::PrintOptions(std::ostream& os) const {
973 os << '[';
974 switch (kind) {
975 case Kind::kAdd:
976 os << "Add, ";
977 break;
978 case Kind::kSub:
979 os << "Sub, ";
980 break;
981 case Kind::kMul:
982 os << "Mul, ";
983 break;
984 case Kind::kDiv:
985 os << "Div, ";
986 break;
987 case Kind::kMod:
988 os << "Mod, ";
989 break;
990 case Kind::kMin:
991 os << "Min, ";
992 break;
993 case Kind::kMax:
994 os << "Max, ";
995 break;
996 case Kind::kPower:
997 os << "Power, ";
998 break;
999 case Kind::kAtan2:
1000 os << "Atan2, ";
1001 break;
1002 }
1003 os << rep;
1004 os << ']';
1005}
1006
1007void Word32PairBinopOp::PrintOptions(std::ostream& os) const {
1008 os << '[';
1009 switch (kind) {
1010 case Kind::kAdd:
1011 os << "Add";
1012 break;
1013 case Kind::kSub:
1014 os << "Sub";
1015 break;
1016 case Kind::kMul:
1017 os << "Mul";
1018 break;
1019 case Kind::kShiftLeft:
1020 os << "ShiftLeft";
1021 break;
1023 os << "ShiftRightSigned";
1024 break;
1026 os << "ShiftRightUnsigned";
1027 break;
1028 }
1029 os << ']';
1030}
1031
1032void WordBinopDeoptOnOverflowOp::PrintOptions(std::ostream& os) const {
1033 os << '[';
1034 switch (kind) {
1035 case Kind::kSignedAdd:
1036 os << "signed add, ";
1037 break;
1038 case Kind::kSignedMul:
1039 os << "signed mul, ";
1040 break;
1041 case Kind::kSignedSub:
1042 os << "signed sub, ";
1043 break;
1044 case Kind::kSignedDiv:
1045 os << "signed div, ";
1046 break;
1047 case Kind::kSignedMod:
1048 os << "signed mod, ";
1049 break;
1050 case Kind::kUnsignedDiv:
1051 os << "unsigned div, ";
1052 break;
1053 case Kind::kUnsignedMod:
1054 os << "unsigned mod, ";
1055 break;
1056 }
1057 os << rep << ", " << mode;
1058 os << ']';
1059}
1060
1061void OverflowCheckedBinopOp::PrintOptions(std::ostream& os) const {
1062 os << '[';
1063 switch (kind) {
1064 case Kind::kSignedAdd:
1065 os << "signed add, ";
1066 break;
1067 case Kind::kSignedSub:
1068 os << "signed sub, ";
1069 break;
1070 case Kind::kSignedMul:
1071 os << "signed mul, ";
1072 break;
1073 }
1074 os << rep;
1075 os << ']';
1076}
1077
1078std::ostream& operator<<(std::ostream& os, OpIndex idx) {
1079 if (!idx.valid()) {
1080 return os << "<invalid OpIndex>";
1081 }
1082 return os << idx.id();
1083}
1084
1085std::ostream& operator<<(std::ostream& os, BlockIndex b) {
1086 if (!b.valid()) {
1087 return os << "<invalid block>";
1088 }
1089 return os << 'B' << b.id();
1090}
1091
1092std::ostream& operator<<(std::ostream& os, const Block* b) {
1093 return os << b->index();
1094}
1095
1096std::ostream& operator<<(std::ostream& os, OpEffects effects) {
1097 auto produce_consume = [](bool produces, bool consumes) {
1098 if (!produces && !consumes) {
1099 return "🁣";
1100 } else if (produces && !consumes) {
1101 return "🁤";
1102 } else if (!produces && consumes) {
1103 return "🁪";
1104 } else if (produces && consumes) {
1105 return "🁫";
1106 }
1107 UNREACHABLE();
1108 };
1109 os << produce_consume(effects.produces.load_heap_memory,
1110 effects.consumes.load_heap_memory);
1111 os << produce_consume(effects.produces.load_off_heap_memory,
1113 os << "\u2003"; // em space
1114 os << produce_consume(effects.produces.store_heap_memory,
1115 effects.consumes.store_heap_memory);
1116 os << produce_consume(effects.produces.store_off_heap_memory,
1118 os << "\u2003"; // em space
1119 os << produce_consume(effects.produces.before_raw_heap_access,
1121 os << produce_consume(effects.produces.after_raw_heap_access,
1123 os << "\u2003"; // em space
1124 os << produce_consume(effects.produces.control_flow,
1125 effects.consumes.control_flow);
1126 os << "\u2003"; // em space
1127 os << (effects.can_create_identity ? 'i' : '_');
1128 os << ' ' << (effects.can_allocate ? 'a' : '_');
1129 return os;
1130}
1131
1132void SwitchOp::PrintOptions(std::ostream& os) const {
1133 os << '[';
1134 for (const Case& c : cases) {
1135 os << "case " << c.value << ": " << c.destination << ", ";
1136 }
1137 os << " default: " << default_case << ']';
1138}
1139
1140void SwitchOp::Validate(const Graph& graph) const {
1141 // Checking that there are no duplicated cases.
1142 std::unordered_set<int> cases_set;
1143 for (Case cas : cases) {
1144 DCHECK(!cases_set.contains(cas.value));
1145 cases_set.insert(cas.value);
1146 }
1147}
1148
1149std::ostream& operator<<(std::ostream& os, ObjectIsOp::Kind kind) {
1150 switch (kind) {
1152 return os << "ArrayBufferView";
1154 return os << "BigInt";
1156 return os << "BigInt64";
1158 return os << "Callable";
1160 return os << "Constructor";
1162 return os << "DetectableCallable";
1164 return os << "InternalizedString";
1166 return os << "NonCallable";
1168 return os << "Number";
1170 return os << "NumberFitsInt32";
1172 return os << "NumberOrBigInt";
1174 return os << "Receiver";
1176 return os << "ReceiverOrNullOrUndefined";
1178 return os << "Smi";
1180 return os << "String";
1182 return os << "StringOrStringWrapper";
1184 return os << "Symbol";
1186 return os << "Undetectable";
1187 }
1188}
1189
1190std::ostream& operator<<(std::ostream& os,
1191 ObjectIsOp::InputAssumptions input_assumptions) {
1192 switch (input_assumptions) {
1194 return os << "None";
1196 return os << "HeapObject";
1198 return os << "BigInt";
1199 }
1200}
1201
1202std::ostream& operator<<(std::ostream& os, NumericKind kind) {
1203 switch (kind) {
1205 return os << "Float64Hole";
1207 return os << "Finite";
1209 return os << "Integer";
1211 return os << "SafeInteger";
1213 return os << "kInt32";
1214 case NumericKind::kSmi:
1215 return os << "kSmi";
1217 return os << "MinusZero";
1218 case NumericKind::kNaN:
1219 return os << "NaN";
1220 }
1221}
1222
1223std::ostream& operator<<(std::ostream& os, ConvertOp::Kind kind) {
1224 switch (kind) {
1226 return os << "Object";
1228 return os << "Boolean";
1230 return os << "Number";
1232 return os << "NumberOrOddball";
1234 return os << "PlainPrimitive";
1236 return os << "String";
1238 return os << "Smi";
1239 }
1240}
1241
1242std::ostream& operator<<(std::ostream& os,
1244 switch (kind) {
1246 return os << "BigInt";
1248 return os << "Boolean";
1250 return os << "HeapNumber";
1253 return os << "HeapNumberOrUndefined";
1255 return os << "Number";
1257 return os << "Smi";
1259 return os << "String";
1260 }
1261}
1262
1263std::ostream& operator<<(
1264 std::ostream& os,
1266 switch (input_interpretation) {
1268 return os << "Signed";
1270 return os << "Unsigned";
1272 return os << "CharCode";
1274 return os << "CodePoint";
1275 }
1276}
1277
1278std::ostream& operator<<(
1279 std::ostream& os,
1281 switch (kind) {
1283 return os << "Smi";
1284 }
1285}
1286
1287std::ostream& operator<<(
1289 input_interpretation) {
1290 switch (input_interpretation) {
1292 return os << "Signed";
1294 return os << "Unsigned";
1295 }
1296}
1297
1298std::ostream& operator<<(std::ostream& os,
1300 switch (kind) {
1302 return os << "Int32";
1304 return os << "Int64";
1306 return os << "Uint32";
1308 return os << "Bit";
1310 return os << "Float64";
1311 }
1312}
1313
1314std::ostream& operator<<(
1315 std::ostream& os,
1317 switch (input_assumptions) {
1319 return os << "Boolean";
1321 return os << "Smi";
1323 return os << "NumberOrOddball";
1325 return os << "PlainPrimitive";
1326 }
1327}
1328
1329std::ostream& operator<<(
1330 std::ostream& os,
1332 switch (kind) {
1334 return os << "Int32";
1337 return os << "AdditiveSafeInteger";
1339 return os << "Int64";
1341 return os << "Float64";
1343 return os << "ArrayIndex";
1344 }
1345}
1346
1347std::ostream& operator<<(
1348 std::ostream& os,
1350 switch (kind) {
1353 return os << "AdditiveSafeInteger";
1355 return os << "Number";
1358 return os << "NumberOrBoolean";
1361 return os << "NumberOrOddball";
1364 return os << "NumberOrString";
1366 return os << "Smi";
1367 }
1368}
1369
1370std::ostream& operator<<(std::ostream& os,
1372 switch (kind) {
1374 return os << "Int32";
1376 return os << "Int64";
1378 return os << "Bit";
1379 }
1380}
1381
1382std::ostream& operator<<(
1383 std::ostream& os,
1385 switch (input_assumptions) {
1387 return os << "BigInt";
1389 return os << "NumberOrOddball";
1391 return os << "HeapObject";
1393 return os << "Object";
1394 }
1395}
1396
1397std::ostream& operator<<(
1398 std::ostream& os,
1400 switch (kind) {
1402 return os << "Int32";
1403 }
1404}
1405
1406std::ostream& operator<<(std::ostream& os, NewArrayOp::Kind kind) {
1407 switch (kind) {
1409 return os << "Double";
1411 return os << "Object";
1412 }
1413}
1414
1415std::ostream& operator<<(std::ostream& os, DoubleArrayMinMaxOp::Kind kind) {
1416 switch (kind) {
1418 return os << "Min";
1420 return os << "Max";
1421 }
1422}
1423
1424std::ostream& operator<<(std::ostream& os, BigIntBinopOp::Kind kind) {
1425 switch (kind) {
1427 return os << "Add";
1429 return os << "Sub";
1431 return os << "Mul";
1433 return os << "Div";
1435 return os << "Mod";
1437 return os << "BitwiseAnd";
1439 return os << "BitwiseOr";
1441 return os << "BitwiseXor";
1443 return os << "ShiftLeft";
1445 return os << "ShiftRightArithmetic";
1446 }
1447}
1448
1449std::ostream& operator<<(std::ostream& os, BigIntComparisonOp::Kind kind) {
1450 switch (kind) {
1452 return os << "Equal";
1454 return os << "LessThan";
1456 return os << "LessThanOrEqual";
1457 }
1458}
1459
1460std::ostream& operator<<(std::ostream& os, BigIntUnaryOp::Kind kind) {
1461 switch (kind) {
1463 return os << "Negate";
1464 }
1465}
1466
1467std::ostream& operator<<(std::ostream& os, StringAtOp::Kind kind) {
1468 switch (kind) {
1470 return os << "CharCode";
1472 return os << "CodePoint";
1473 }
1474}
1475
1476#ifdef V8_INTL_SUPPORT
1477std::ostream& operator<<(std::ostream& os, StringToCaseIntlOp::Kind kind) {
1478 switch (kind) {
1479 case StringToCaseIntlOp::Kind::kLower:
1480 return os << "Lower";
1481 case StringToCaseIntlOp::Kind::kUpper:
1482 return os << "Upper";
1483 }
1484}
1485#endif // V8_INTL_SUPPORT
1486
1487std::ostream& operator<<(std::ostream& os, StringComparisonOp::Kind kind) {
1488 switch (kind) {
1490 return os << "Equal";
1492 return os << "LessThan";
1494 return os << "LessThanOrEqual";
1495 }
1496}
1497
1498std::ostream& operator<<(std::ostream& os, ArgumentsLengthOp::Kind kind) {
1499 switch (kind) {
1501 return os << "Arguments";
1503 return os << "Rest";
1504 }
1505}
1506
1507std::ostream& operator<<(std::ostream& os,
1509 switch (kind) {
1511 return os << "Element";
1513 return os << "NumberElement";
1515 return os << "OddballElement";
1517 return os << "NonNumberElement";
1519 return os << "SignedSmallElement";
1520 }
1521}
1522
1523void PrintMapSet(std::ostream& os, const ZoneRefSet<Map>& maps) {
1524 os << "{";
1525 for (size_t i = 0; i < maps.size(); ++i) {
1526 if (i != 0) os << ",";
1527 os << JSONEscaped(maps[i].object());
1528 }
1529 os << "}";
1530}
1531
1532void CompareMapsOp::PrintOptions(std::ostream& os) const {
1533 os << "[";
1534 PrintMapSet(os, maps);
1535 os << "]";
1536}
1537
1538void CheckMapsOp::PrintOptions(std::ostream& os) const {
1539 os << "[";
1540 PrintMapSet(os, maps);
1541 os << ", " << flags << ", " << feedback << "]";
1542}
1543
1544void AssumeMapOp::PrintOptions(std::ostream& os) const {
1545 os << "[";
1546 PrintMapSet(os, maps);
1547 os << "]";
1548}
1549
1550std::ostream& operator<<(std::ostream& os, SameValueOp::Mode mode) {
1551 switch (mode) {
1553 return os << "SameValue";
1555 return os << "SameValueNumbersOnly";
1556 }
1557}
1558
1559std::ostream& operator<<(std::ostream& os, FindOrderedHashEntryOp::Kind kind) {
1560 switch (kind) {
1562 return os << "FindOrderedHashMapEntry";
1564 return os << "FindOrderedHashMapEntryForInt32Key";
1566 return os << "FindOrderedHashSetEntry";
1567 }
1568}
1569
1570std::ostream& operator<<(std::ostream& os, JSStackCheckOp::Kind kind) {
1571 switch (kind) {
1573 return os << "function-entry";
1575 return os << "builtin-entry";
1577 return os << "loop";
1578 }
1579}
1580
1581#if V8_ENABLE_WEBASSEMBLY
1582
1583const RegisterRepresentation& RepresentationFor(wasm::ValueType type) {
1584 static const RegisterRepresentation kWord32 =
1586 static const RegisterRepresentation kWord64 =
1588 static const RegisterRepresentation kFloat32 =
1590 static const RegisterRepresentation kFloat64 =
1592 static const RegisterRepresentation kTagged =
1594 static const RegisterRepresentation kSimd128 =
1596
1597 switch (type.kind()) {
1598 case wasm::kI8:
1599 case wasm::kI16:
1600 case wasm::kI32:
1601 return kWord32;
1602 case wasm::kI64:
1603 return kWord64;
1604 case wasm::kF16:
1605 case wasm::kF32:
1606 return kFloat32;
1607 case wasm::kF64:
1608 return kFloat64;
1609 case wasm::kRefNull:
1610 case wasm::kRef:
1611 return kTagged;
1612 case wasm::kS128:
1613 return kSimd128;
1614 case wasm::kVoid:
1615 case wasm::kTop:
1616 case wasm::kBottom:
1617 UNREACHABLE();
1618 }
1619}
1620
1621namespace {
1622template <size_t size>
1623void PrintSimdValue(std::ostream& os, const uint8_t (&value)[size]) {
1624 os << "0x" << std::hex << std::setfill('0');
1625#ifdef V8_TARGET_BIG_ENDIAN
1626 for (int i = 0; i < static_cast<int>(size); i++) {
1627#else
1628 for (int i = static_cast<int>(size) - 1; i >= 0; i--) {
1629#endif
1630 os << std::setw(2) << static_cast<int>(value[i]);
1631 }
1632 os << std::dec << std::setfill(' ');
1633}
1634} // namespace
1635
1636void Simd128ConstantOp::PrintOptions(std::ostream& os) const {
1637 PrintSimdValue(os, value);
1638}
1639
1640std::ostream& operator<<(std::ostream& os, Simd128BinopOp::Kind kind) {
1641 switch (kind) {
1642#define PRINT_KIND(kind) \
1643 case Simd128BinopOp::Kind::k##kind: \
1644 return os << #kind;
1645 FOREACH_SIMD_128_BINARY_OPCODE(PRINT_KIND)
1646 }
1647#undef PRINT_KIND
1648}
1649
1650std::ostream& operator<<(std::ostream& os, Simd128UnaryOp::Kind kind) {
1651 switch (kind) {
1652#define PRINT_KIND(kind) \
1653 case Simd128UnaryOp::Kind::k##kind: \
1654 return os << #kind;
1655 FOREACH_SIMD_128_UNARY_OPCODE(PRINT_KIND)
1656 }
1657#undef PRINT_KIND
1658}
1659
1660std::ostream& operator<<(std::ostream& os, Simd128ReduceOp::Kind kind) {
1661 switch (kind) {
1662#define PRINT_KIND(kind) \
1663 case Simd128ReduceOp::Kind::k##kind: \
1664 return os << #kind;
1665 FOREACH_SIMD_128_REDUCE_OPTIONAL_OPCODE(PRINT_KIND)
1666 }
1667#undef PRINT_KIND
1668}
1669
1670std::ostream& operator<<(std::ostream& os, Simd128ShiftOp::Kind kind) {
1671 switch (kind) {
1672#define PRINT_KIND(kind) \
1673 case Simd128ShiftOp::Kind::k##kind: \
1674 return os << #kind;
1675 FOREACH_SIMD_128_SHIFT_OPCODE(PRINT_KIND)
1676 }
1677#undef PRINT_KIND
1678}
1679
1680std::ostream& operator<<(std::ostream& os, Simd128TestOp::Kind kind) {
1681 switch (kind) {
1682#define PRINT_KIND(kind) \
1683 case Simd128TestOp::Kind::k##kind: \
1684 return os << #kind;
1685 FOREACH_SIMD_128_TEST_OPCODE(PRINT_KIND)
1686 }
1687#undef PRINT_KIND
1688}
1689
1690std::ostream& operator<<(std::ostream& os, Simd128SplatOp::Kind kind) {
1691 switch (kind) {
1692#define PRINT_KIND(kind) \
1693 case Simd128SplatOp::Kind::k##kind: \
1694 return os << #kind;
1695 FOREACH_SIMD_128_SPLAT_OPCODE(PRINT_KIND)
1696 }
1697#undef PRINT_KIND
1698}
1699
1700std::ostream& operator<<(std::ostream& os, Simd128TernaryOp::Kind kind) {
1701 switch (kind) {
1702#define PRINT_KIND(kind) \
1703 case Simd128TernaryOp::Kind::k##kind: \
1704 return os << #kind;
1705 FOREACH_SIMD_128_TERNARY_OPCODE(PRINT_KIND)
1706 }
1707#undef PRINT_KIND
1708}
1709
1710void Simd128ExtractLaneOp::PrintOptions(std::ostream& os) const {
1711 os << '[';
1712 switch (kind) {
1713 case Kind::kI8x16S:
1714 os << "I8x16S";
1715 break;
1716 case Kind::kI8x16U:
1717 os << "I8x16U";
1718 break;
1719 case Kind::kI16x8S:
1720 os << "I16x8S";
1721 break;
1722 case Kind::kI16x8U:
1723 os << "I16x8U";
1724 break;
1725 case Kind::kI32x4:
1726 os << "I32x4";
1727 break;
1728 case Kind::kI64x2:
1729 os << "I64x2";
1730 break;
1731 case Kind::kF16x8:
1732 os << "F16x8";
1733 break;
1734 case Kind::kF32x4:
1735 os << "F32x4";
1736 break;
1737 case Kind::kF64x2:
1738 os << "F64x2";
1739 break;
1740 }
1741 os << ", " << static_cast<int32_t>(lane) << ']';
1742}
1743
1744void Simd128ReplaceLaneOp::PrintOptions(std::ostream& os) const {
1745 os << '[';
1746 switch (kind) {
1747 case Kind::kI8x16:
1748 os << "I8x16";
1749 break;
1750 case Kind::kI16x8:
1751 os << "I16x8";
1752 break;
1753 case Kind::kI32x4:
1754 os << "I32x4";
1755 break;
1756 case Kind::kI64x2:
1757 os << "I64x2";
1758 break;
1759 case Kind::kF16x8:
1760 os << "F16x8";
1761 break;
1762 case Kind::kF32x4:
1763 os << "F32x4";
1764 break;
1765 case Kind::kF64x2:
1766 os << "F64x2";
1767 break;
1768 }
1769 os << ", " << static_cast<int32_t>(lane) << ']';
1770}
1771
1772void Simd128LaneMemoryOp::PrintOptions(std::ostream& os) const {
1773 os << '[' << (mode == Mode::kLoad ? "Load" : "Store") << ", ";
1774 if (kind.maybe_unaligned) os << "unaligned, ";
1775 if (kind.with_trap_handler) os << "protected, ";
1776 switch (lane_kind) {
1777 case LaneKind::k8:
1778 os << '8';
1779 break;
1780 case LaneKind::k16:
1781 os << "16";
1782 break;
1783 case LaneKind::k32:
1784 os << "32";
1785 break;
1786 case LaneKind::k64:
1787 os << "64";
1788 break;
1789 }
1790 os << "bit, lane: " << static_cast<int>(lane);
1791 if (offset != 0) os << ", offset: " << offset;
1792 os << ']';
1793}
1794
1795void Simd128LoadTransformOp::PrintOptions(std::ostream& os) const {
1796 os << '[';
1797 if (load_kind.maybe_unaligned) os << "unaligned, ";
1798 if (load_kind.with_trap_handler) os << "protected, ";
1799
1800 switch (transform_kind) {
1801#define PRINT_KIND(kind) \
1802 case TransformKind::k##kind: \
1803 os << #kind; \
1804 break;
1805 FOREACH_SIMD_128_LOAD_TRANSFORM_OPCODE(PRINT_KIND)
1806#undef PRINT_KIND
1807 }
1808
1809 os << ", offset: " << offset << ']';
1810}
1811
1812void Simd128ShuffleOp::PrintOptions(std::ostream& os) const {
1813 os << '[';
1814 switch (kind) {
1815 case Kind::kI8x2:
1816 os << "I8x2, ";
1817 break;
1818 case Kind::kI8x4:
1819 os << "I8x4, ";
1820 break;
1821 case Kind::kI8x8:
1822 os << "I8x8, ";
1823 break;
1824 case Kind::kI8x16:
1825 os << "I8x16, ";
1826 break;
1827 }
1828 PrintSimdValue(os, shuffle);
1829 os << ']';
1830}
1831
1832#if V8_ENABLE_WASM_DEINTERLEAVED_MEM_OPS
1833void Simd128LoadPairDeinterleaveOp::PrintOptions(std::ostream& os) const {
1834 os << '[';
1835 if (load_kind.maybe_unaligned) os << "unaligned, ";
1836 if (load_kind.with_trap_handler) os << "protected, ";
1837
1838 switch (kind) {
1839 case Kind::k8x32:
1840 os << "8x32";
1841 break;
1842 case Kind::k16x16:
1843 os << "16x16";
1844 break;
1845 case Kind::k32x8:
1846 os << "32x8";
1847 break;
1848 case Kind::k64x4:
1849 os << "64x4";
1850 break;
1851 }
1852 os << ']';
1853}
1854#endif // V8_ENABLE_WASM_DEINTERLEAVED_MEM_OPS
1855
1856#if V8_ENABLE_WASM_SIMD256_REVEC
1857void Simd256ConstantOp::PrintOptions(std::ostream& os) const {
1858 PrintSimdValue(os, value);
1859}
1860
1861void Simd256Extract128LaneOp::PrintOptions(std::ostream& os) const {
1862 os << '[' << static_cast<int>(lane) << ']';
1863}
1864
1865void Simd256LoadTransformOp::PrintOptions(std::ostream& os) const {
1866 os << '[';
1867 if (load_kind.maybe_unaligned) os << "unaligned, ";
1868 if (load_kind.with_trap_handler) os << "protected, ";
1869
1870 switch (transform_kind) {
1871#define PRINT_KIND(kind) \
1872 case TransformKind::k##kind: \
1873 os << #kind; \
1874 break;
1875 FOREACH_SIMD_256_LOAD_TRANSFORM_OPCODE(PRINT_KIND)
1876#undef PRINT_KIND
1877 }
1878
1879 os << ", offset: " << offset << ']';
1880}
1881
1882std::ostream& operator<<(std::ostream& os, Simd256UnaryOp::Kind kind) {
1883 switch (kind) {
1884#define PRINT_KIND(kind) \
1885 case Simd256UnaryOp::Kind::k##kind: \
1886 return os << #kind;
1887 FOREACH_SIMD_256_UNARY_OPCODE(PRINT_KIND)
1888 }
1889#undef PRINT_KIND
1890}
1891
1892std::ostream& operator<<(std::ostream& os, Simd256TernaryOp::Kind kind) {
1893 switch (kind) {
1894#define PRINT_KIND(kind) \
1895 case Simd256TernaryOp::Kind::k##kind: \
1896 return os << #kind;
1897 FOREACH_SIMD_256_TERNARY_OPCODE(PRINT_KIND)
1898 }
1899#undef PRINT_KIND
1900}
1901
1902std::ostream& operator<<(std::ostream& os, Simd256BinopOp::Kind kind) {
1903 switch (kind) {
1904#define PRINT_KIND(kind) \
1905 case Simd256BinopOp::Kind::k##kind: \
1906 return os << #kind;
1907 FOREACH_SIMD_256_BINARY_OPCODE(PRINT_KIND)
1908 }
1909#undef PRINT_KIND
1910}
1911
1912std::ostream& operator<<(std::ostream& os, Simd256ShiftOp::Kind kind) {
1913 switch (kind) {
1914#define PRINT_KIND(kind) \
1915 case Simd256ShiftOp::Kind::k##kind: \
1916 return os << #kind;
1917 FOREACH_SIMD_256_SHIFT_OPCODE(PRINT_KIND)
1918 }
1919#undef PRINT_KIND
1920}
1921
1922std::ostream& operator<<(std::ostream& os, Simd256SplatOp::Kind kind) {
1923 switch (kind) {
1924#define PRINT_KIND(kind) \
1925 case Simd256SplatOp::Kind::k##kind: \
1926 return os << #kind;
1927 FOREACH_SIMD_256_SPLAT_OPCODE(PRINT_KIND)
1928 }
1929#undef PRINT_KIND
1930}
1931
1932#ifdef V8_TARGET_ARCH_X64
1933void Simd256ShufdOp::PrintOptions(std::ostream& os) const {
1934 os << '[' << std::bitset<8>(control) << ']';
1935}
1936
1937void Simd256ShufpsOp::PrintOptions(std::ostream& os) const {
1938 os << '[' << std::bitset<8>(control) << ']';
1939}
1940
1941std::ostream& operator<<(std::ostream& os, Simd256UnpackOp::Kind kind) {
1942 switch (kind) {
1943#define PRINT_KIND(kind) \
1944 case Simd256UnpackOp::Kind::k##kind: \
1945 return os << #kind;
1946 FOREACH_SIMD_256_UNPACK_OPCODE(PRINT_KIND)
1947 }
1948#undef PRINT_KIND
1949}
1950#endif // V8_TARGET_ARCH_X64
1951#endif // V8_ENABLE_WASM_SIMD256_REVEC
1952
1953void WasmAllocateArrayOp::PrintOptions(std::ostream& os) const {
1954 os << '[' << array_type->element_type() << ']';
1955}
1956
1957void ArrayGetOp::PrintOptions(std::ostream& os) const {
1958 os << '[' << (is_signed ? "signed " : "")
1959 << (array_type->mutability() ? "" : "immutable ")
1960 << array_type->element_type() << ']';
1961}
1962
1963#endif // V8_ENABLE_WEBASSEBMLY
1964
1965std::string Operation::ToString() const {
1966 std::stringstream ss;
1967 ss << *this;
1968 return ss.str();
1969}
1970
1974
1977 if (initialized_) return;
1978 initialized_ = true;
1979
1982#define SET_SUPPORTED(name, machine_name) \
1983 instance_.name##_ = supported & MachineOperatorBuilder::Flag::k##machine_name;
1984
1986#undef SET_SUPPORTED
1987}
1988
1990 const Graph& graph) {
1991 return SuccessorBlocks(block.LastOperation(graph));
1992}
1993
1994// static
1999
2000// static
2005
2006void CheckExceptionOp::Validate(const Graph& graph) const {
2008 // `CheckException` should follow right after the throwing operation.
2010 V<Any>::Cast(graph.PreviousIndex(graph.Index(*this))));
2011}
2012
2013namespace {
2014BlockIndex index_for_bound_block(const Block* block) {
2015 DCHECK_NOT_NULL(block);
2016 const BlockIndex index = block->index();
2017 DCHECK(index.valid());
2018 return index;
2019}
2020} // namespace
2021
2022size_t CallOp::hash_value(HashingStrategy strategy) const {
2023 if (strategy == HashingStrategy::kMakeSnapshotStable) {
2024 // Destructure here to cause a compilation error in case `options` is
2025 // changed.
2026 auto [descriptor_value, effects] = options();
2027 return HashWithOptions(*descriptor, effects);
2028 } else {
2029 return Base::hash_value(strategy);
2030 }
2031}
2032
2034 if (strategy == HashingStrategy::kMakeSnapshotStable) {
2035 // Destructure here to cause a compilation error in case `options` is
2036 // changed.
2037 auto [didnt_throw_block_value, catch_block_value] = options();
2038 return HashWithOptions(index_for_bound_block(didnt_throw_block_value),
2039 index_for_bound_block(catch_block_value));
2040 } else {
2041 return Base::hash_value(strategy);
2042 }
2043}
2044
2045size_t GotoOp::hash_value(HashingStrategy strategy) const {
2046 if (strategy == HashingStrategy::kMakeSnapshotStable) {
2047 // Destructure here to cause a compilation error in case `options` is
2048 // changed.
2049 auto [destination_value, is_backedge_value] = options();
2050 return HashWithOptions(index_for_bound_block(destination_value),
2051 is_backedge_value);
2052 } else {
2053 return Base::hash_value(strategy);
2054 }
2055}
2056
2058 if (strategy == HashingStrategy::kMakeSnapshotStable) {
2059 // Destructure here to cause a compilation error in case `options` is
2060 // changed.
2061 auto [if_true_value, if_false_value, hint_value] = options();
2062 return HashWithOptions(index_for_bound_block(if_true_value),
2063 index_for_bound_block(if_false_value), hint_value);
2064 } else {
2065 return Base::hash_value(strategy);
2066 }
2067}
2068
2070 if (strategy == HashingStrategy::kMakeSnapshotStable) {
2071 // Destructure here to cause a compilation error in case `options` is
2072 // changed.
2073 auto [cases_value, default_case_value, default_hint_value] = options();
2074 DCHECK_NOT_NULL(default_case_value);
2075 size_t hash = HashWithOptions(index_for_bound_block(default_case_value),
2076 default_hint_value);
2077 for (const auto& c : cases_value) {
2078 hash = fast_hash_combine(hash, c.value,
2079 index_for_bound_block(c.destination), c.hint);
2080 }
2081 return hash;
2082 } else {
2083 return Base::hash_value(strategy);
2084 }
2085}
2086
2087namespace {
2088// Ensures basic consistency of representation mapping.
2089class InputsRepFactoryCheck : InputsRepFactory {
2090 static_assert(*ToMaybeRepPointer(RegisterRepresentation::Word32()) ==
2092 static_assert(*ToMaybeRepPointer(RegisterRepresentation::Word64()) ==
2094 static_assert(*ToMaybeRepPointer(RegisterRepresentation::Float32()) ==
2096 static_assert(*ToMaybeRepPointer(RegisterRepresentation::Float64()) ==
2098 static_assert(*ToMaybeRepPointer(RegisterRepresentation::Tagged()) ==
2100 static_assert(*ToMaybeRepPointer(RegisterRepresentation::Compressed()) ==
2102 static_assert(*ToMaybeRepPointer(RegisterRepresentation::Simd128()) ==
2104};
2105} // namespace
2106
2107bool IsUnlikelySuccessor(const Block* block, const Block* successor,
2108 const Graph& graph) {
2109 DCHECK(base::contains(successor->Predecessors(), block));
2110 const Operation& terminator = block->LastOperation(graph);
2111 switch (terminator.opcode) {
2112 case Opcode::kCheckException: {
2113 const CheckExceptionOp& check_exception =
2114 terminator.Cast<CheckExceptionOp>();
2115 return successor == check_exception.catch_block;
2116 }
2117 case Opcode::kGoto:
2118 return false;
2119 case Opcode::kBranch: {
2120 const BranchOp& branch = terminator.Cast<BranchOp>();
2121 return (branch.hint == BranchHint::kTrue &&
2122 successor == branch.if_false) ||
2123 (branch.hint == BranchHint::kFalse && successor == branch.if_true);
2124 }
2125 case Opcode::kSwitch: {
2126 const SwitchOp& swtch = terminator.Cast<SwitchOp>();
2127 if (successor == swtch.default_case) {
2128 return swtch.default_hint == BranchHint::kFalse;
2129 }
2130 auto it = std::find_if(swtch.cases.begin(), swtch.cases.end(),
2131 [successor](const SwitchOp::Case& c) {
2132 return c.destination == successor;
2133 });
2134 DCHECK_NE(it, swtch.cases.end());
2135 return it->hint == BranchHint::kFalse;
2136 }
2137 case Opcode::kDeoptimize:
2138 case Opcode::kTailCall:
2139 case Opcode::kUnreachable:
2140 case Opcode::kReturn:
2141 UNREACHABLE();
2142
2143#define NON_TERMINATOR_CASE(op) case Opcode::k##op:
2145 UNREACHABLE();
2146#undef NON_TERMINATOR_CASE
2147 }
2148}
2149
2150bool Operation::IsOnlyUserOf(const Operation& value, const Graph& graph) const {
2151 DCHECK_GE(std::count(inputs().begin(), inputs().end(), graph.Index(value)),
2152 1);
2153 if (value.saturated_use_count.IsOne()) return true;
2154 return std::count(inputs().begin(), inputs().end(), graph.Index(value)) ==
2155 value.saturated_use_count.Get();
2156}
2157
2158#if V8_ENABLE_WEBASSEMBLY
2159bool Operation::IsProtectedLoad() const {
2160 if (const auto* load = TryCast<LoadOp>()) {
2161 return load->kind.with_trap_handler;
2162 } else if (const auto* load_t = TryCast<Simd128LoadTransformOp>()) {
2163 return load_t->load_kind.with_trap_handler;
2164#ifdef V8_ENABLE_WASM_DEINTERLEAVED_MEM_OPS
2165 } else if (const auto* load_t = TryCast<Simd128LoadPairDeinterleaveOp>()) {
2166 return load_t->load_kind.with_trap_handler;
2167#endif // V8_ENABLE_WASM_DEINTERLEAVED_MEM_OPS
2168 }
2169 return false;
2170}
2171#endif // V8_ENABLE_WEBASSEMBLY
2172
2173} // namespace v8::internal::compiler::turboshaft
Builtins::Kind kind
Definition builtins.cc:40
constexpr size_t size() const
Definition vector.h:70
static ExternalReference Create(const SCTableReference &table_ref)
float get_scalar() const
Definition boxed-float.h:38
uint64_t get_bits() const
Definition boxed-float.h:80
double get_scalar() const
Definition boxed-float.h:81
constexpr MachineRepresentation representation() const
static constexpr MachineType AnyCompressed()
static constexpr MachineType CompressedPointer()
static MachineOperatorBuilder::AlignmentRequirements AlignmentRequirements()
static MachineOperatorBuilder::Flags SupportedMachineOperatorFlags()
base::SmallVector< Block *, 8 > Predecessors() const
Definition graph.h:328
static constexpr FloatRepresentation Float32()
static constexpr FloatRepresentation Float64()
static constexpr MaybeRegisterRepresentation Float64()
static constexpr MaybeRegisterRepresentation Compressed()
static constexpr MaybeRegisterRepresentation Word64()
static constexpr MaybeRegisterRepresentation Float32()
static constexpr MaybeRegisterRepresentation Tagged()
static constexpr MaybeRegisterRepresentation Word32()
static constexpr MaybeRegisterRepresentation Simd128()
static constexpr MemoryRepresentation AnyTagged()
static constexpr MemoryRepresentation TaggedPointer()
constexpr uint32_t id() const
Definition index.h:61
static constexpr RegisterRepresentation Compressed()
static constexpr RegisterRepresentation FromMachineRepresentation(MachineRepresentation rep)
static constexpr RegisterRepresentation Simd128()
static constexpr RegisterRepresentation Word32()
static constexpr RegisterRepresentation Float64()
static constexpr RegisterRepresentation Float32()
constexpr bool AllowImplicitRepresentationChangeTo(RegisterRepresentation dst_rep, bool graph_created_from_turbofan) const
static constexpr RegisterRepresentation Word64()
static constexpr RegisterRepresentation Tagged()
static bool IsUnalignedStoreSupported(MemoryRepresentation repr)
static bool IsUnalignedLoadSupported(MemoryRepresentation repr)
int end
JSHeapBroker * broker
int32_t offset
#define LAZY_MUTEX_INITIALIZER
Definition mutex.h:105
int int32_t
Definition unicode.cc:40
bool contains(const C &container, const T &element)
std::ostream & operator<<(std::ostream &os, PaddingSpace padding)
V8_INLINE const Operation & Get(const Graph &graph, OpIndex index)
Definition graph.h:1231
constexpr bool MayThrow(Opcode opcode)
Definition operations.h:432
V8_INLINE size_t fast_hash_combine()
Definition fast-hash.h:19
const char * OpcodeName(Opcode opcode)
std::optional< Builtin > TryGetBuiltinId(const ConstantOp *target, JSHeapBroker *broker)
Definition operations.cc:45
Zone * get_zone(Graph *graph)
Definition operations.cc:43
constexpr std::underlying_type_t< Opcode > OpcodeIndex(Opcode x)
Definition operations.h:373
constexpr uint16_t kNumberOfOpcodes
Definition operations.h:412
void PrintMapSet(std::ostream &os, const ZoneRefSet< Map > &maps)
base::SmallVector< Block *, 4 > SuccessorBlocks(const Block &block, const Graph &graph)
bool IsUnlikelySuccessor(const Block *block, const Block *successor, const Graph &graph)
Runtime::FunctionId GetBuiltinForStackCheckKind(StackCheckKind kind)
Definition globals.h:38
Node::Uses::const_iterator begin(const Node::Uses &uses)
Definition node.h:708
ref_traits< T >::ref_type MakeRef(JSHeapBroker *broker, Tagged< T > object)
std::ostream & operator<<(std::ostream &os, AtomicMemoryOrder order)
const char * GetAbortReason(AbortReason reason)
auto PrintCollection(const T &collection) -> PrintIteratorRange< typename std::common_type< decltype(std::begin(collection)), decltype(std::end(collection))>::type >
Definition ostreams.h:186
bool is_signed(Condition cond)
Operation
Definition operation.h:43
#define SET_SUPPORTED(name, machine_name)
#define SWITCH_CASE(Name)
#define OPCODE_NAME(Name)
#define PRINT_KIND(Name)
#define NON_TERMINATOR_CASE(op)
#define GENERIC_BINOP_LIST(V)
#define SUPPORTED_OPERATIONS_LIST(V)
#define TURBOSHAFT_OPERATION_LIST(V)
Definition operations.h:362
#define TURBOSHAFT_THROWING_STATIC_OUTPUTS_OPERATIONS_LIST(V)
Definition operations.h:421
#define TURBOSHAFT_OPERATION_LIST_NOT_BLOCK_TERMINATOR(V)
Definition operations.h:354
#define GENERIC_UNOP_LIST(V)
#define DCHECK_NOT_NULL(val)
Definition logging.h:492
#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
void PrintOptions(std::ostream &os) const
void PrintInputs(std::ostream &os, const std::string &op_index_prefix) const
void PrintOptions(std::ostream &os) const
void PrintInputs(std::ostream &os, const std::string &op_index_prefix) const
size_t hash_value(HashingStrategy strategy=HashingStrategy::kDefault) const
size_t hash_value(HashingStrategy strategy=HashingStrategy::kDefault) const
void PrintOptions(std::ostream &os) const
Definition operations.cc:91
V8_EXPORT_PRIVATE bool IsStackCheck(const Graph &graph, JSHeapBroker *broker, StackCheckKind kind) const
Definition operations.cc:64
size_t hash_value(HashingStrategy strategy=HashingStrategy::kDefault) const
V8_EXPORT_PRIVATE void Validate(const Graph &graph) const
IndirectHandle< i::HeapObject > handle() const
void PrintOptions(std::ostream &os) const
union v8::internal::compiler::turboshaft::ConstantOp::Storage storage
V8_EXPORT_PRIVATE void Validate(const Graph &graph) const
base::Vector< const RegisterRepresentation > outputs_rep() const
static V8_EXPORT_PRIVATE bool IsSupported(Kind kind, FloatRepresentation rep)
V8_EXPORT_PRIVATE void PrintOptions(std::ostream &os) const
base::Vector< const OpIndex > state_values() const
V8_EXPORT_PRIVATE void Validate(const Graph &graph) const
size_t hash_value(HashingStrategy strategy=HashingStrategy::kDefault) const
void PrintOptions(std::ostream &os) const
void PrintInputs(std::ostream &os, const std::string &op_index_prefix) const
size_t hash_value(HashingStrategy strategy=HashingStrategy::kDefault) const
size_t HashWithOptions(const Args &... args) const
void PrintOptions(std::ostream &os) const
void PrintInputs(std::ostream &os, const std::string &op_index_prefix) const
bool IsOnlyUserOf(const Operation &value, const Graph &graph) const
base::Vector< const OpIndex > inputs() const
const underlying_operation_t< Op > * TryCast() const
Definition operations.h:990
underlying_operation_t< Op > & Cast()
Definition operations.h:980
base::Vector< const RegisterRepresentation > outputs_rep() const
void PrintOptions(std::ostream &os) const
void PrintOptions(std::ostream &os) const
void PrintInputs(std::ostream &os, const std::string &op_index_prefix) const
void PrintOptions(std::ostream &os) const
V8_EXPORT_PRIVATE void Validate(const Graph &graph) const
size_t hash_value(HashingStrategy strategy=HashingStrategy::kDefault) const
void PrintOptions(std::ostream &os) const
Definition operations.cc:95
void PrintOptions(std::ostream &os) const
static V8_EXPORT_PRIVATE bool IsSupported(Kind kind, WordRepresentation rep)
wasm::ValueType type