v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
operations.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_TURBOSHAFT_OPERATIONS_H_
6#define V8_COMPILER_TURBOSHAFT_OPERATIONS_H_
7
8#include <cmath>
9#include <cstdint>
10#include <cstring>
11#include <limits>
12#include <optional>
13#include <tuple>
14#include <type_traits>
15#include <utility>
16
17#include "src/base/logging.h"
18#include "src/base/macros.h"
22#include "src/base/vector.h"
24#include "src/common/globals.h"
38#include "src/flags/flags.h"
39
40#if V8_ENABLE_WEBASSEMBLY
43#endif
44
45namespace v8::internal {
46class HeapObject;
47V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
48 AbortReason reason);
49} // namespace v8::internal
50namespace v8::internal::compiler {
51class CallDescriptor;
52class JSWasmCallParameters;
53class DeoptimizeParameters;
54class FrameStateInfo;
55class Node;
56enum class TrapId : int32_t;
57} // namespace v8::internal::compiler
59
60inline constexpr char kCompilationZoneName[] = "compilation-zone";
61
62class Block;
63struct FrameStateData;
64class Graph;
65struct FrameStateOp;
66
67enum class HashingStrategy {
69 // This strategy requires that hashing a graph during builtin construction
70 // (mksnapshot) produces the same hash for repeated runs of mksnapshot. This
71 // requires that no pointers and external constants are used in hashes.
73};
74
75// This belongs to `VariableReducer` in `variable-reducer.h`. It is defined here
76// because of cyclic header dependencies.
83
84// DEFINING NEW OPERATIONS
85// =======================
86// For each operation `Foo`, we define:
87// - An entry V(Foo) in one of the TURBOSHAFT*OPERATION list (eg,
88// TURBOSHAFT_OPERATION_LIST_BLOCK_TERMINATOR,
89// TURBOSHAFT_SIMPLIFIED_OPERATION_LIST etc), which defines
90// `Opcode::kFoo` and whether the operation is a block terminator.
91// - A `struct FooOp`, which derives from either `OperationT<FooOp>` or
92// `FixedArityOperationT<k, FooOp>` if the op always has excactly `k` inputs.
93// Furthermore, the struct has to contain:
94// - A bunch of options directly as public fields.
95// - A getter `options()` returning a tuple of all these options. This is used
96// for default printing and hashing. Alternatively, `void
97// PrintOptions(std::ostream& os) const` and `size_t hash_value() const` can
98// also be defined manually.
99// - Getters for named inputs.
100// - A constructor that first takes all the inputs and then all the options. For
101// a variable arity operation where the constructor doesn't take the inputs as
102// a single base::Vector<OpIndex> argument, it's also necessary to overwrite
103// the static `New` function, see `CallOp` for an example.
104// - An `Explode` method that unpacks an operation and invokes the passed
105// callback. If the operation inherits from FixedArityOperationT, the base
106// class already provides the required implementation.
107// - `OpEffects` as either a static constexpr member `effects` or a
108// non-static method `Effects()` if the effects depend on the particular
109// operation and not just the opcode.
110// - outputs_rep/inputs_rep methods, which should return a vector describing the
111// representation of the outputs and inputs of this operations.
112// After defining the struct here, you'll also need to integrate it in
113// Turboshaft:
114// - If Foo is not lowered before reaching the instruction selector, handle
115// Opcode::kFoo in the Turboshaft VisitNode of instruction-selector.cc.
116
117#ifdef V8_INTL_SUPPORT
118#define TURBOSHAFT_INTL_OPERATION_LIST(V) V(StringToCaseIntl)
119#else
120#define TURBOSHAFT_INTL_OPERATION_LIST(V)
121#endif // V8_INTL_SUPPORT
122
123#ifdef V8_ENABLE_WEBASSEMBLY
124// These operations should be lowered to Machine operations during
125// WasmLoweringPhase.
126#define TURBOSHAFT_WASM_OPERATION_LIST(V) \
127 V(WasmStackCheck) \
128 V(GlobalGet) \
129 V(GlobalSet) \
130 V(RootConstant) \
131 V(IsRootConstant) \
132 V(Null) \
133 V(IsNull) \
134 V(AssertNotNull) \
135 V(RttCanon) \
136 V(WasmTypeCheck) \
137 V(WasmTypeCast) \
138 V(AnyConvertExtern) \
139 V(ExternConvertAny) \
140 V(WasmTypeAnnotation) \
141 V(StructGet) \
142 V(StructSet) \
143 V(ArrayGet) \
144 V(ArraySet) \
145 V(ArrayLength) \
146 V(WasmAllocateArray) \
147 V(WasmAllocateStruct) \
148 V(WasmRefFunc) \
149 V(StringAsWtf16) \
150 V(StringPrepareForGetCodeUnit)
151
152#ifdef V8_ENABLE_WASM_DEINTERLEAVED_MEM_OPS
153#define TURBOSHAFT_DEINTERLEAVED_OPERATION_LIST(V) \
154 V(Simd128LoadPairDeinterleave)
155#else
156#define TURBOSHAFT_DEINTERLEAVED_OPERATION_LIST(V)
157#endif
158
159#if V8_ENABLE_WASM_SIMD256_REVEC
160#define TURBOSHAFT_SIMD256_COMMOM_OPERATION_LIST(V) \
161 V(Simd256Constant) \
162 V(Simd256Extract128Lane) \
163 V(Simd256LoadTransform) \
164 V(Simd256Unary) \
165 V(Simd256Binop) \
166 V(Simd256Shift) \
167 V(Simd256Ternary) \
168 V(Simd256Splat) \
169 V(SimdPack128To256)
170
171#if V8_TARGET_ARCH_X64
172#define TURBOSHAFT_SIMD256_X64_OPERATION_LIST(V) \
173 V(Simd256Shufd) \
174 V(Simd256Shufps) \
175 V(Simd256Unpack)
176
177#define TURBOSHAFT_SIMD256_OPERATION_LIST(V) \
178 TURBOSHAFT_SIMD256_COMMOM_OPERATION_LIST(V) \
179 TURBOSHAFT_SIMD256_X64_OPERATION_LIST(V)
180#else
181#define TURBOSHAFT_SIMD256_OPERATION_LIST(V) \
182 TURBOSHAFT_SIMD256_COMMOM_OPERATION_LIST(V)
183#endif // V8_TARGET_ARCH_X64
184
185#else
186#define TURBOSHAFT_SIMD256_OPERATION_LIST(V)
187#endif
188
189#define TURBOSHAFT_SIMD_OPERATION_LIST(V) \
190 V(Simd128Constant) \
191 V(Simd128Binop) \
192 V(Simd128Unary) \
193 V(Simd128Reduce) \
194 V(Simd128Shift) \
195 V(Simd128Test) \
196 V(Simd128Splat) \
197 V(Simd128Ternary) \
198 V(Simd128ExtractLane) \
199 V(Simd128ReplaceLane) \
200 V(Simd128LaneMemory) \
201 V(Simd128LoadTransform) \
202 V(Simd128Shuffle) \
203 TURBOSHAFT_SIMD256_OPERATION_LIST(V) \
204 TURBOSHAFT_DEINTERLEAVED_OPERATION_LIST(V)
205
206#else
207#define TURBOSHAFT_WASM_OPERATION_LIST(V)
208#define TURBOSHAFT_SIMD_OPERATION_LIST(V)
209#endif
210
211#define TURBOSHAFT_OPERATION_LIST_BLOCK_TERMINATOR(V) \
212 V(CheckException) \
213 V(Goto) \
214 V(TailCall) \
215 V(Unreachable) \
216 V(Return) \
217 V(Branch) \
218 V(Switch) \
219 V(Deoptimize)
220
221#ifdef V8_ENABLE_CONTINUATION_PRESERVED_EMBEDDER_DATA
222#define TURBOSHAFT_CPED_OPERATION_LIST(V) \
223 V(GetContinuationPreservedEmbedderData) \
224 V(SetContinuationPreservedEmbedderData)
225#else
226#define TURBOSHAFT_CPED_OPERATION_LIST(V)
227#endif // V8_ENABLE_CONTINUATION_PRESERVED_EMBEDDER_DATA
228
229// These operations should be lowered to Machine operations during
230// MachineLoweringPhase.
231#define TURBOSHAFT_SIMPLIFIED_OPERATION_LIST(V) \
232 TURBOSHAFT_INTL_OPERATION_LIST(V) \
233 TURBOSHAFT_CPED_OPERATION_LIST(V) \
234 V(ArgumentsLength) \
235 V(BigIntBinop) \
236 V(BigIntComparison) \
237 V(BigIntUnary) \
238 V(CheckedClosure) \
239 V(WordBinopDeoptOnOverflow) \
240 V(CheckEqualsInternalizedString) \
241 V(CheckMaps) \
242 V(CompareMaps) \
243 V(Float64Is) \
244 V(ObjectIs) \
245 V(ObjectIsNumericValue) \
246 V(Float64SameValue) \
247 V(SameValue) \
248 V(ChangeOrDeopt) \
249 V(Convert) \
250 V(ConvertJSPrimitiveToObject) \
251 V(ConvertJSPrimitiveToUntagged) \
252 V(ConvertJSPrimitiveToUntaggedOrDeopt) \
253 V(ConvertUntaggedToJSPrimitive) \
254 V(ConvertUntaggedToJSPrimitiveOrDeopt) \
255 V(TruncateJSPrimitiveToUntagged) \
256 V(TruncateJSPrimitiveToUntaggedOrDeopt) \
257 V(DoubleArrayMinMax) \
258 V(EnsureWritableFastElements) \
259 V(FastApiCall) \
260 V(FindOrderedHashEntry) \
261 V(LoadDataViewElement) \
262 V(LoadFieldByIndex) \
263 V(LoadMessage) \
264 V(LoadStackArgument) \
265 V(LoadTypedElement) \
266 V(StoreDataViewElement) \
267 V(StoreMessage) \
268 V(StoreTypedElement) \
269 V(MaybeGrowFastElements) \
270 V(NewArgumentsElements) \
271 V(NewArray) \
272 V(RuntimeAbort) \
273 V(StaticAssert) \
274 V(StringAt) \
275 V(StringComparison) \
276 V(StringConcat) \
277 V(StringFromCodePointAt) \
278 V(StringIndexOf) \
279 V(StringLength) \
280 V(TypedArrayLength) \
281 V(StringSubstring) \
282 V(NewConsString) \
283 V(TransitionAndStoreArrayElement) \
284 V(TransitionElementsKind) \
285 V(TransitionElementsKindOrCheckMap) \
286 V(DebugPrint) \
287 V(CheckTurboshaftTypeOf) \
288 V(Word32SignHint)
289
290// These Operations are the lowest level handled by Turboshaft, and are
291// supported by the InstructionSelector.
292#define TURBOSHAFT_MACHINE_OPERATION_LIST(V) \
293 V(WordBinop) \
294 V(FloatBinop) \
295 V(Word32PairBinop) \
296 V(OverflowCheckedBinop) \
297 V(WordUnary) \
298 V(OverflowCheckedUnary) \
299 V(FloatUnary) \
300 V(Shift) \
301 V(Comparison) \
302 V(Change) \
303 V(TryChange) \
304 V(BitcastWord32PairToFloat64) \
305 V(TaggedBitcast) \
306 V(Select) \
307 V(PendingLoopPhi) \
308 V(Constant) \
309 V(LoadRootRegister) \
310 V(Load) \
311 V(Store) \
312 V(Retain) \
313 V(Parameter) \
314 V(OsrValue) \
315 V(StackPointerGreaterThan) \
316 V(StackSlot) \
317 V(FrameConstant) \
318 V(DeoptimizeIf) \
319 IF_WASM(V, TrapIf) \
320 IF_WASM(V, LoadStackPointer) \
321 IF_WASM(V, SetStackPointer) \
322 V(Phi) \
323 V(FrameState) \
324 V(Call) \
325 V(CatchBlockBegin) \
326 V(DidntThrow) \
327 V(Tuple) \
328 V(Projection) \
329 V(DebugBreak) \
330 V(AssumeMap) \
331 V(AtomicRMW) \
332 V(AtomicWord32Pair) \
333 V(MemoryBarrier) \
334 V(Comment) \
335 V(Dead) \
336 V(AbortCSADcheck)
337
338#define TURBOSHAFT_JS_THROWING_OPERATION_LIST(V) \
339 V(GenericBinop) \
340 V(GenericUnop) \
341 V(ToNumberOrNumeric)
342
343#define TURBOSHAFT_JS_OPERATION_LIST(V) \
344 TURBOSHAFT_JS_THROWING_OPERATION_LIST(V)
345
346// These are operations that are not Machine operations and need to be lowered
347// before Instruction Selection, but they are not lowered during the
348// MachineLoweringPhase.
349#define TURBOSHAFT_OTHER_OPERATION_LIST(V) \
350 V(Allocate) \
351 V(DecodeExternalPointer) \
352 V(JSStackCheck)
353
354#define TURBOSHAFT_OPERATION_LIST_NOT_BLOCK_TERMINATOR(V) \
355 TURBOSHAFT_WASM_OPERATION_LIST(V) \
356 TURBOSHAFT_SIMD_OPERATION_LIST(V) \
357 TURBOSHAFT_MACHINE_OPERATION_LIST(V) \
358 TURBOSHAFT_SIMPLIFIED_OPERATION_LIST(V) \
359 TURBOSHAFT_JS_OPERATION_LIST(V) \
360 TURBOSHAFT_OTHER_OPERATION_LIST(V)
361
362#define TURBOSHAFT_OPERATION_LIST(V) \
363 TURBOSHAFT_OPERATION_LIST_BLOCK_TERMINATOR(V) \
364 TURBOSHAFT_OPERATION_LIST_NOT_BLOCK_TERMINATOR(V)
365
366enum class Opcode : uint8_t {
367#define ENUM_CONSTANT(Name) k##Name,
369#undef ENUM_CONSTANT
370};
371
372const char* OpcodeName(Opcode opcode);
373constexpr std::underlying_type_t<Opcode> OpcodeIndex(Opcode x) {
374 return static_cast<std::underlying_type_t<Opcode>>(x);
375}
376
377#define FORWARD_DECLARE(Name) struct Name##Op;
379#undef FORWARD_DECLARE
380
381namespace detail {
382template <class Op>
384
385#define OPERATION_OPCODE_MAP_CASE(Name) \
386 template <> \
387 struct operation_to_opcode_map<Name##Op> \
388 : std::integral_constant<Opcode, Opcode::k##Name> {};
390#undef OPERATION_OPCODE_MAP_CASE
391} // namespace detail
392
393template <typename Op>
395 : detail::operation_to_opcode_map<std::remove_cvref_t<Op>> {};
396template <typename Op>
398
399template <typename Op, uint64_t Mask, uint64_t Value>
400struct OpMaskT {
401 using operation = Op;
402 static constexpr uint64_t mask = Mask;
403 static constexpr uint64_t value = Value;
404};
405
406#define COUNT_OPCODES(Name) +1
409#undef COUNT_OPCODES
410
411#define COUNT_OPCODES(Name) +1
412constexpr uint16_t kNumberOfOpcodes =
414#undef COUNT_OPCODES
415
416inline constexpr bool IsBlockTerminator(Opcode opcode) {
418}
419
420// Operations that can throw and that have static output representations.
421#define TURBOSHAFT_THROWING_STATIC_OUTPUTS_OPERATIONS_LIST(V) \
422 TURBOSHAFT_JS_THROWING_OPERATION_LIST(V)
423
424// This list repeats the operations that may throw and need to be followed by
425// `DidntThrow`.
426#define TURBOSHAFT_THROWING_OPERATIONS_LIST(V) \
427 TURBOSHAFT_THROWING_STATIC_OUTPUTS_OPERATIONS_LIST(V) \
428 V(Call) \
429 V(FastApiCall)
430
431// Operations that need to be followed by `DidntThrowOp`.
432inline constexpr bool MayThrow(Opcode opcode) {
433#define CASE(Name) case Opcode::k##Name:
434 switch (opcode) {
436 return true;
437 default:
438 return false;
439 }
440#undef CASE
441}
442
443// For Throwing operations, outputs_rep() are empty, because the values are
444// produced by the subsequent DidntThrow. Nevertheless, the operation has to
445// define its output representations in an array that DidntThrow can then reuse
446// to know what its outputs are. Additionally, when using Maglev as a frontend,
447// catch handlers that have never been reach so far are not emitted, and instead
448// the throwing operations lazy deopt instead of throwing.
449//
450// That's where the THROWING_OP_BOILERPLATE macro comes in: it creates an array
451// of representations that DidntThrow can use, and will define outputs_rep() to
452// be empty, and takes care of creating a LazyDeoptOnThrow member. For instance:
453//
454// THROWING_OP_BOILERPLATE(RegisterRepresentation::Tagged(),
455// RegisterRepresentation::Word32())
456//
457// Warning: don't forget to add `lazy_deopt_on_throw` to the `options` of your
458// Operation (you'll get a compile-time error if you forget it).
459#define THROWING_OP_BOILERPLATE(...) \
460 static constexpr RegisterRepresentation kOutputRepsStorage[]{__VA_ARGS__}; \
461 static constexpr base::Vector<const RegisterRepresentation> kOutReps = \
462 base::VectorOf(kOutputRepsStorage, arraysize(kOutputRepsStorage)); \
463 base::Vector<const RegisterRepresentation> outputs_rep() const { \
464 return {}; \
465 } \
466 LazyDeoptOnThrow lazy_deopt_on_throw;
467
468template <typename T>
470 ZoneVector<T>& storage,
471 std::initializer_list<RegisterRepresentation> values) {
472 storage.resize(values.size());
473 size_t i = 0;
474 for (auto&& value : values) {
475 storage[i++] = value;
476 }
477 return base::VectorOf(storage);
478}
479
481 public:
486
491
492 protected:
495 size_t index = static_cast<size_t>(rep.value()) * 2;
496 DCHECK_LT(index, arraysize(rep_map));
497 return &rep_map[index];
498 }
499
500 private:
521};
522
524 // Produced by loads, consumed by operations that should not move before loads
525 // because they change memory.
528
529 // Produced by stores, consumed by operations that should not move before
530 // stores because they load or store memory.
533
534 // Operations that perform raw heap access (like initialization) consume
535 // `before_raw_heap_access` and produce `after_raw_heap_access`.
536 // Operations that need the heap to be in a consistent state produce
537 // `before_raw_heap_access` and consume `after_raw_heap_access`.
539 // Produced by operations that access raw/untagged pointers into the
540 // heap or keep such a pointer alive, consumed by operations that can GC to
541 // ensure they don't move before the raw access.
543
544 // Produced by any operation that can affect whether subsequent operations are
545 // executed, for example by branching, deopting, throwing or aborting.
546 // Consumed by all operations that should not be hoisted before a check
547 // because they rely on it. For example, loads usually rely on the shape of
548 // the heap object or the index being in bounds.
549 bool control_flow : 1;
550 // We need to ensure that the padding bits have a specified value, as they are
551 // observable in bitwise operations.
552 uint8_t unused_padding : 1;
553
554 using Bits = uint8_t;
556 : load_heap_memory(false),
558 store_heap_memory(false),
562 control_flow(false),
563 unused_padding(0) {}
564 Bits bits() const { return base::bit_cast<Bits>(*this); }
567 }
568 bool operator==(EffectDimensions other) const {
569 return bits() == other.bits();
570 }
571 bool operator!=(EffectDimensions other) const {
572 return bits() != other.bits();
573 }
574};
575static_assert(sizeof(EffectDimensions) == sizeof(EffectDimensions::Bits));
576
577// Possible reorderings are restricted using two bit vectors: `produces` and
578// `consumes`. Two operations cannot be reordered if the first operation
579// produces an effect dimension that the second operation consumes. This is not
580// necessarily symmetric. For example, it is possible to reorder
581// Load(x)
582// CheckMaps(y)
583// to become
584// CheckMaps(x)
585// Load(y)
586// because the load cannot affect the map check. But the other direction could
587// be unsound, if the load depends on the map check having been executed. The
588// former reordering is useful to push a load across a check into a branch if
589// it is only needed there. The effect system expresses this by having the map
590// check produce `EffectDimensions::control_flow` and the load consuming
591// `EffectDimensions::control_flow`. If the producing operation comes before the
592// consuming operation, then this order has to be preserved. But if the
593// consuming operation comes first, then we are free to reorder them. Operations
594// that produce and consume the same effect dimension always have a fixed order
595// among themselves. For example, stores produce and consume the store
596// dimensions. It is possible for operations to be reorderable unless certain
597// other operations appear in-between. This way, the IR can be generous with
598// reorderings as long as all operations are high-level, but become more
599// restrictive as soon as low-level operations appear. For example, allocations
600// can be freely reordered. Tagged bitcasts can be reordered with other tagged
601// bitcasts. But a tagged bitcast cannot be reordered with allocations, as this
602// would mean that an untagged pointer can be alive while a GC is happening. The
603// way this works is that allocations produce the `before_raw_heap_access`
604// dimension and consume the `after_raw_heap_access` dimension to stay either
605// before or after a raw heap access. This means that there are no ordering
606// constraints between allocations themselves. Bitcasts should not
607// be moved accross an allocation. We treat them as raw heap access by letting
608// them consume `before_raw_heap_access` and produce `after_raw_heap_access`.
609// This way, allocations cannot be moved across bitcasts. Similarily,
610// initializing stores and uninitialized allocations are classified as raw heap
611// access, to prevent any operation that relies on a consistent heap state to be
612// scheduled in the middle of an inline allocation. As long as we didn't lower
613// to raw heap accesses yet, pure allocating operations or operations reading
614// immutable memory can float freely. As soon as there are raw heap accesses,
615// they become more restricted in their movement. Note that calls are not the
616// most side-effectful operations, as they do not leave the heap in an
617// inconsistent state, so they do not need to be marked as raw heap access.
618struct OpEffects {
621
622 // Operations that cannot be merged because they produce identity. That is,
623 // every repetition can produce a different result, but the order in which
624 // they are executed does not matter. All we care about is that they are
625 // different. Producing a random number or allocating an object with
626 // observable pointer equality are examples. Producing identity doesn't
627 // restrict reordering in straight-line code, but we must prevent using GVN or
628 // moving identity-producing operations in- or out of loops.
630 // If the operation can allocate and therefore can trigger GC.
631 bool can_allocate : 1;
632 // Instructions that have no uses but are `required_when_unused` should not be
633 // removed.
635 // We need to ensure that the padding bits have a specified value, as they are
636 // observable in bitwise operations. This is split into two fields so that
637 // also MSVC creates the correct object layout.
638 uint8_t unused_padding_1 : 5;
640
641 constexpr OpEffects()
642 : can_create_identity(false),
643 can_allocate(false),
646 unused_padding_2(0) {}
647
648 using Bits = uint32_t;
649 Bits bits() const { return base::bit_cast<Bits>(*this); }
650 static OpEffects FromBits(Bits bits) {
651 return base::bit_cast<OpEffects>(bits);
652 }
653
654 bool operator==(OpEffects other) const { return bits() == other.bits(); }
655 bool operator!=(OpEffects other) const { return bits() != other.bits(); }
657 return FromBits(bits() | other.bits());
658 }
660 return FromBits(bits() & other.bits());
661 }
662 bool IsSubsetOf(OpEffects other) const {
663 return (bits() & ~other.bits()) == 0;
664 }
665
667 OpEffects result = *this;
668 // Do not move the operation into a region with raw heap access.
670 result.consumes.after_raw_heap_access = true;
671 return result;
672 }
673 // Like `CanAllocate()`, but allocated values must be immutable and not have
674 // identity (for example `HeapNumber`).
675 // Note that if we first allocate something as mutable and later make it
676 // immutable, we have to allocate it with identity.
679 result.can_allocate = true;
680 return result;
681 }
682 // Allocations change the GC state and can trigger GC, as well as produce a
683 // fresh identity.
684 constexpr OpEffects CanAllocate() const {
686 }
687 // The operation can leave the heap in an incosistent state or have untagged
688 // pointers into the heap as input or output.
689 constexpr OpEffects CanDoRawHeapAccess() const {
690 OpEffects result = *this;
691 // Do not move any operation that relies on a consistent heap state accross.
693 result.consumes.before_raw_heap_access = true;
694 return result;
695 }
696 // Reading mutable heap memory. Reading immutable memory doesn't count.
697 constexpr OpEffects CanReadHeapMemory() const {
698 OpEffects result = *this;
700 // Do not reorder before stores.
701 result.consumes.store_heap_memory = true;
702 return result;
703 }
704 // Reading mutable off-heap memory or other input. Reading immutable memory
705 // doesn't count.
706 constexpr OpEffects CanReadOffHeapMemory() const {
707 OpEffects result = *this;
709 // Do not reorder before stores.
710 result.consumes.store_off_heap_memory = true;
711 return result;
712 }
713 // Writing any off-memory or other output.
715 OpEffects result = *this;
717 result.produces.store_off_heap_memory = true;
718 // Do not reorder before stores.
719 result.consumes.store_off_heap_memory = true;
720 // Do not reorder before loads.
721 result.consumes.load_off_heap_memory = true;
722 // Do not move before deopting or aborting operations.
723 result.consumes.control_flow = true;
724 return result;
725 }
726 // Writing heap memory that existed before the operation started. Initializing
727 // newly allocated memory doesn't count.
728 constexpr OpEffects CanWriteHeapMemory() const {
729 OpEffects result = *this;
731 result.produces.store_heap_memory = true;
732 // Do not reorder before stores.
733 result.consumes.store_heap_memory = true;
734 // Do not reorder before loads.
735 result.consumes.load_heap_memory = true;
736 // Do not move before deopting or aborting operations.
737 result.consumes.control_flow = true;
738 return result;
739 }
740 // Writing any memory or other output, on- or off-heap.
741 constexpr OpEffects CanWriteMemory() const {
743 }
744 // Reading any memory or other input, on- or off-heap.
745 constexpr OpEffects CanReadMemory() const {
747 }
748 // The operation might read immutable data from the heap, so it can be freely
749 // reordered with operations that keep the heap in a consistent state. But we
750 // must prevent the operation from observing an incompletely initialized
751 // object.
754 return result;
755 }
756 // Partial operations that are only safe to execute after we performed certain
757 // checks, for example loads may only be safe after a corresponding bound or
758 // map checks.
759 constexpr OpEffects CanDependOnChecks() const {
760 OpEffects result = *this;
762 return result;
763 }
764 // The operation can affect control flow (like branch, deopt, throw or crash).
765 constexpr OpEffects CanChangeControlFlow() const {
766 OpEffects result = *this;
768 // Signal that this changes control flow. Prevents stores or operations
769 // relying on checks from flowing before this operation.
770 result.produces.control_flow = true;
771 // Stores must not flow past something that affects control flow.
772 result.consumes.store_heap_memory = true;
773 result.consumes.store_off_heap_memory = true;
774 return result;
775 }
776 // Execution of the current function may end with this operation, for example
777 // because of return, deopt, exception throw or abort/trap.
779 // All memory becomes observable.
781 }
782 // The operation can deopt.
783 constexpr OpEffects CanDeopt() const {
785 // We might depend on previous checks to avoid deopting.
787 }
788 // Producing identity doesn't prevent reorderings, but it prevents GVN from
789 // de-duplicating identical operations.
790 constexpr OpEffects CanCreateIdentity() const {
791 OpEffects result = *this;
793 return result;
794 }
795 // The set of all possible effects.
796 constexpr OpEffects CanCallAnything() const {
797 return CanReadMemory()
799 .CanAllocate()
803 }
804 constexpr OpEffects RequiredWhenUnused() const {
805 OpEffects result = *this;
807 return result;
808 }
809
810 // Operations that can be removed if their result is not used. Unused
811 // allocations can be removed.
812 constexpr bool is_required_when_unused() const {
814 }
815 // Operations that can be moved before a preceding branch or check.
817 // Since this excludes `CanDependOnChecks()`, most loads actually cannot be
818 // hoisted.
820 }
821 // Operations that can be eliminated via value numbering, which means that if
822 // there are two identical operations where one dominates the other, then the
823 // second can be replaced with the first one. This is safe for deopting or
824 // throwing operations, because the absence of read effects guarantees
825 // deterministic behavior.
842 // Operations that CanDependOnChecks can still be constant-folded. If they
843 // did indeed depend on a check, then their result will only be used after
844 // said check has been executed anyways.
846 }
847};
848static_assert(sizeof(OpEffects) == sizeof(OpEffects::Bits));
849
851 return static_cast<size_t>(effects.bits());
852}
853
855 return first.produces.bits() & (second.consumes.bits());
856}
857
859 EffectDimensions produces = first.produces;
860 // The control flow effects produced by Loads are due to trap handler. We can
861 // ignore this kind of effect when swapping two Loads that both have trap
862 // handler.
863 produces.control_flow = false;
864 return produces.bits() & (second.consumes.bits());
865}
866
867V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
868 OpEffects op_effects);
869
870// SaturatedUint8 is a wrapper around a uint8_t, which can be incremented and
871// decremented with the `Incr` and `Decr` methods. These methods prevent over-
872// and underflow, and saturate once the uint8_t reaches the maximum (255):
873// future increment and decrement will not change the value then.
874// We purposefuly do not expose the uint8_t directly, so that users go through
875// Incr/Decr/SetToZero/SetToOne to manipulate it, so that the saturation and
876// lack of over/underflow is always respected.
878 public:
879 SaturatedUint8() = default;
880
881 void Incr() {
882 if (V8_LIKELY(val != kMax)) {
883 val++;
884 }
885 }
886 void Decr() {
887 if (V8_LIKELY(val != 0 && val != kMax)) {
888 val--;
889 }
890 }
891
892 void SetToZero() { val = 0; }
893 void SetToOne() { val = 1; }
894
895 bool IsZero() const { return val == 0; }
896 bool IsOne() const { return val == 1; }
897 bool IsSaturated() const { return val == kMax; }
898 uint8_t Get() const { return val; }
899
901 uint32_t sum = val;
902 sum += other.val;
903 val = static_cast<uint8_t>(std::min<uint32_t>(sum, kMax));
904 return *this;
905 }
906
907 static SaturatedUint8 FromSize(size_t value) {
908 uint8_t val = static_cast<uint8_t>(std::min<size_t>(value, kMax));
909 return SaturatedUint8{val};
910 }
911
912 private:
913 explicit SaturatedUint8(uint8_t val) : val(val) {}
914 uint8_t val = 0;
915 static constexpr uint8_t kMax = std::numeric_limits<uint8_t>::max();
916};
917
918// underlying_operation<> is used to extract the operation type from OpMaskT
919// classes used in Operation::Is<> and Operation::TryCast<>.
920template <typename T>
922 using type = T;
923};
924template <typename T, uint64_t M, uint64_t V>
926 using type = T;
927};
928template <typename T>
930
931// Baseclass for all Turboshaft operations.
932// The `alignas(OpIndex)` is necessary because it is followed by an array of
933// `OpIndex` inputs.
934struct alignas(OpIndex) Operation {
936 OpIndex Map(OpIndex index) { return index; }
938 template <size_t N>
942 };
943
945
946 // The number of uses of this operation in the current graph.
947 // Instead of overflowing, we saturate the value if it reaches the maximum. In
948 // this case, the true number of uses is unknown.
949 // We use such a small type to save memory and because nodes with a high
950 // number of uses are rare. Additionally, we usually only care if the number
951 // of uses is 0, 1 or bigger than 1.
953
954 const uint16_t input_count;
955
956 // The inputs are stored adjacent in memory, right behind the `Operation`
957 // object.
958 base::Vector<const OpIndex> inputs() const;
959 V8_INLINE OpIndex input(size_t i) const { return inputs()[i]; }
960
961 static size_t StorageSlotCount(Opcode opcode, size_t input_count);
962 size_t StorageSlotCount() const {
963 return StorageSlotCount(opcode, input_count);
964 }
965
969
970 template <class Op>
971 bool Is() const {
972 if constexpr (std::is_base_of_v<Operation, Op>) {
973 return opcode == Op::opcode;
974 } else {
975 // Otherwise this must be OpMaskT.
976 return IsOpmask<Op>();
977 }
978 }
979 template <class Op>
981 DCHECK(Is<Op>());
982 return *static_cast<underlying_operation_t<Op>*>(this);
983 }
984 template <class Op>
986 DCHECK(Is<Op>());
987 return *static_cast<const underlying_operation_t<Op>*>(this);
988 }
989 template <class Op>
991 if (!Is<Op>()) return nullptr;
992 return static_cast<const underlying_operation_t<Op>*>(this);
993 }
994 template <class Op>
996 if (!Is<Op>()) return nullptr;
997 return static_cast<underlying_operation_t<Op>*>(this);
998 }
999 OpEffects Effects() const;
1000 bool IsBlockTerminator() const {
1001 return turboshaft::IsBlockTerminator(opcode);
1002 }
1004 DCHECK_IMPLIES(IsBlockTerminator(), Effects().is_required_when_unused());
1005 return Effects().is_required_when_unused();
1006 }
1007 bool IsProtectedLoad() const;
1008
1009 std::string ToString() const;
1010 void PrintInputs(std::ostream& os, const std::string& op_index_prefix) const;
1011 void PrintOptions(std::ostream& os) const;
1012
1013 // Returns true if {this} is the only operation using {value}.
1014 bool IsOnlyUserOf(const Operation& value, const Graph& graph) const;
1015
1016 void Print() const;
1017
1018 protected:
1019 // Operation objects store their inputs behind the object. Therefore, they can
1020 // only be constructed as part of a Graph.
1021 explicit Operation(Opcode opcode, size_t input_count)
1022 : opcode(opcode), input_count(input_count) {
1023 DCHECK_LE(input_count,
1024 std::numeric_limits<decltype(this->input_count)>::max());
1025 }
1026
1027 template <class OpmaskT>
1028 // A Turboshaft operation can be as small as 4 Bytes while Opmasks can span up
1029 // to 8 Bytes. Any mask larger than the operation it is compared with will
1030 // always have a mismatch in the initialized memory. Still, there can be some
1031 // uninitialized memory being compared as part of the 8 Byte comparison that
1032 // this function performs.
1033 V8_CLANG_NO_SANITIZE("memory") bool IsOpmask() const {
1034 static_assert(std::is_same_v<
1036 typename OpMaskT<typename OpmaskT::operation, OpmaskT::mask,
1037 OpmaskT::value>::operation>);
1038 // We check with the given mask.
1039 uint64_t b;
1040 memcpy(&b, this, sizeof(uint64_t));
1041 b &= OpmaskT::mask;
1042 return b == OpmaskT::value;
1043 }
1044
1045 Operation(const Operation&) = delete;
1046 Operation& operator=(const Operation&) = delete;
1047};
1048
1051 const char* op_index_prefix = "#";
1052};
1053
1054V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
1056inline std::ostream& operator<<(std::ostream& os, const Operation& op) {
1057 return os << OperationPrintStyle{op};
1058}
1059std::ostream& operator<<(std::ostream& os, RootIndex index);
1060
1062
1063OperationStorageSlot* AllocateOpStorage(Graph* graph, size_t slot_count);
1064V8_EXPORT_PRIVATE const Operation& Get(const Graph& graph, OpIndex index);
1065
1066// Determine if an operation declares `effects`, which means that its
1067// effects are static and don't depend on inputs or options.
1068template <class Op, class = void>
1069struct HasStaticEffects : std::bool_constant<false> {};
1070template <class Op>
1071struct HasStaticEffects<Op, std::void_t<decltype(Op::effects)>>
1072 : std::bool_constant<true> {};
1073
1074// This template knows the complete type of the operation and is plugged into
1075// the inheritance hierarchy. It removes boilerplate from the concrete
1076// `Operation` subclasses, defining everything that can be expressed
1077// generically. It overshadows many methods from `Operation` with ones that
1078// exploit additional static information.
1079template <class Derived>
1081 // Enable concise base-constructor call in derived struct.
1083
1084 static const Opcode opcode;
1085
1086 static constexpr OpEffects Effects() { return Derived::effects; }
1087 static constexpr bool IsBlockTerminator() {
1088 return turboshaft::IsBlockTerminator(opcode);
1089 }
1091 return IsBlockTerminator() ||
1092 derived_this().Effects().is_required_when_unused();
1093 }
1094
1095 static constexpr std::optional<OpEffects> EffectsIfStatic() {
1096 if constexpr (HasStaticEffects<Derived>::value) {
1097 return Derived::Effects();
1098 }
1099 return std::nullopt;
1100 }
1101
1102 Derived& derived_this() { return *static_cast<Derived*>(this); }
1103 const Derived& derived_this() const {
1104 return *static_cast<const Derived*>(this);
1105 }
1106
1107 // Shadow Operation::inputs to exploit static knowledge about object size.
1109 return {reinterpret_cast<OpIndex*>(reinterpret_cast<char*>(this) +
1110 sizeof(Derived)),
1111 derived_this().input_count};
1112 }
1114 return {reinterpret_cast<const OpIndex*>(
1115 reinterpret_cast<const char*>(this) + sizeof(Derived)),
1116 derived_this().input_count};
1117 }
1118
1119 V8_INLINE OpIndex& input(size_t i) { return derived_this().inputs()[i]; }
1120 // TODO(chromium:331100916): remove this V<Any> overload once all users use
1121 // the more specific V<T> overload.
1122 V8_INLINE V<Any> input(size_t i) const { return derived_this().inputs()[i]; }
1123 template <typename T>
1124 V8_INLINE V<T> input(size_t i) const {
1125 return V<T>::Cast(derived_this().inputs()[i]);
1126 }
1127
1128 static size_t StorageSlotCount(size_t input_count) {
1129 // The operation size in bytes is:
1130 // `sizeof(Derived) + input_count*sizeof(OpIndex)`.
1131 // This is an optimized computation of:
1132 // round_up(size_in_bytes / sizeof(StorageSlot))
1133 constexpr size_t r = sizeof(OperationStorageSlot) / sizeof(OpIndex);
1134 static_assert(sizeof(OperationStorageSlot) % sizeof(OpIndex) == 0);
1135 static_assert(sizeof(Derived) % sizeof(OpIndex) == 0);
1136 size_t result = std::max<size_t>(
1137 2, (r - 1 + sizeof(Derived) / sizeof(OpIndex) + input_count) / r);
1138 DCHECK_EQ(result, Operation::StorageSlotCount(opcode, input_count));
1139 return result;
1140 }
1141 size_t StorageSlotCount() const { return StorageSlotCount(input_count); }
1142
1143 template <class... Args>
1144 static Derived& New(Graph* graph, size_t input_count, Args... args) {
1146 AllocateOpStorage(graph, StorageSlotCount(input_count));
1147 Derived* result = new (ptr) Derived(args...);
1148#ifdef DEBUG
1149 result->Validate(*graph);
1152 result->inputs_rep(storage);
1153 // TODO(mliedtke): DCHECK that expected and inputs are of the same size
1154 // and adapt inputs_rep() to always emit a representation for all inputs.
1155 size_t end = std::min<size_t>(expected.size(), result->input_count);
1156 for (size_t i = 0; i < end; ++i) {
1157 if (expected[i] == MaybeRegisterRepresentation::None()) continue;
1158 ValidateOpInputRep(*graph, result->inputs()[i],
1159 RegisterRepresentation(expected[i]), result);
1160 }
1161#endif
1162 // If this DCHECK fails, then the number of inputs specified in the
1163 // operation constructor and in the static New function disagree.
1164 DCHECK_EQ(input_count, result->Operation::input_count);
1165 return *result;
1166 }
1167
1168 template <class... Args>
1169 static Derived& New(Graph* graph, ShadowyOpIndexVectorWrapper inputs,
1170 Args... args) {
1171 return New(graph, inputs.size(), inputs, args...);
1172 }
1173
1174 explicit OperationT(size_t input_count) : Operation(opcode, input_count) {
1175 static_assert((std::is_base_of<OperationT, Derived>::value));
1176#if !V8_CC_MSVC
1177 static_assert(std::is_trivially_copyable<Derived>::value);
1178#endif // !V8_CC_MSVC
1179 static_assert(std::is_trivially_destructible<Derived>::value);
1180 }
1182 : OperationT(inputs.size()) {
1183 this->inputs().OverwriteWith(
1184 static_cast<base::Vector<const OpIndex>>(inputs));
1185 }
1186
1187 bool EqualsForGVN(const Base& other) const {
1188 // By default, GVN only removed identical Operations. However, some
1189 // Operations (like DeoptimizeIf) can be GVNed when a dominating
1190 // similar-but-not-identical one exists. In that case, the Operation should
1191 // redefine EqualsForGVN, so that GVN knows which inputs or options of the
1192 // Operation to ignore (you should also probably redefine hash_value,
1193 // otherwise GVN won't even try to call EqualsForGVN).
1194 return derived_this() == other.derived_this();
1195 }
1196 bool operator==(const Base& other) const {
1197 return derived_this().inputs() == other.derived_this().inputs() &&
1198 derived_this().options() == other.derived_this().options();
1199 }
1200 template <typename... Args>
1201 size_t HashWithOptions(const Args&... args) const {
1202 return fast_hash_combine(opcode, derived_this().inputs(), args...);
1203 }
1205 HashingStrategy strategy = HashingStrategy::kDefault) const {
1206 return HashWithOptions(derived_this().options());
1207 }
1208
1209 void PrintInputs(std::ostream& os, const std::string& op_index_prefix) const {
1210 os << "(";
1211 bool first = true;
1212 for (OpIndex input : inputs()) {
1213 if (!first) os << ", ";
1214 first = false;
1215 os << op_index_prefix << input.id();
1216 }
1217 os << ")";
1218 }
1219
1220 void PrintOptions(std::ostream& os) const {
1221 const auto& options = derived_this().options();
1222 constexpr size_t options_count =
1223 std::tuple_size<std::remove_reference_t<decltype(options)>>::value;
1224 if (options_count == 0) {
1225 return;
1226 }
1227 PrintOptionsHelper(os, options, std::make_index_sequence<options_count>());
1228 }
1229
1230 // Check graph invariants for this operation. Will be invoked in debug mode
1231 // immediately upon construction.
1232 // Concrete Operator classes are expected to re-define it.
1233 void Validate(const Graph& graph) const {}
1234
1235 private:
1236 template <class... T, size_t... I>
1237 static void PrintOptionsHelper(std::ostream& os,
1238 const std::tuple<T...>& options,
1239 std::index_sequence<I...>) {
1240 os << "[";
1241 bool first = true;
1242 USE(first);
1243 ((first ? (first = false, os << std::get<I>(options))
1244 : os << ", " << std::get<I>(options)),
1245 ...);
1246 os << "]";
1247 }
1248
1249 // All Operations have to define the outputs_rep function, to which
1250 // Operation::outputs_rep() will forward, based on their opcode. If you forget
1251 // to define it, then Operation::outputs_rep() would forward to itself,
1252 // resulting in an infinite loop. To avoid this, we define here in OperationT
1253 // a private version outputs_rep (with no implementation): if an operation
1254 // forgets to define outputs_rep, then Operation::outputs_rep() tries to call
1255 // this private version, which fails at compile time.
1257
1258 // Returns a vector of the input representations.
1259 // The passed in {storage} can be used to store the underlying data.
1260 // The returned vector might be smaller than the input_count in which case the
1261 // additional inputs are assumed to have no register representation.
1264};
1265
1266template <size_t InputCount, class Derived>
1268 // Enable concise base access in derived struct.
1270
1271 // Shadow Operation::input_count to exploit static knowledge.
1272 static constexpr uint16_t input_count = InputCount;
1273
1274 template <class... Args>
1275 explicit FixedArityOperationT(Args... args)
1276 : OperationT<Derived>(InputCount) {
1277 static_assert(sizeof...(Args) == InputCount, "wrong number of inputs");
1278 size_t i = 0;
1279 OpIndex* inputs = this->inputs().begin();
1280 ((inputs[i++] = args), ...);
1281 }
1282
1283 // Redefine the input initialization to tell C++ about the static input size.
1284 template <class... Args>
1285 static Derived& New(Graph* graph, Args... args) {
1286 Derived& result =
1287 OperationT<Derived>::New(graph, InputCount, std::move(args)...);
1288 return result;
1289 }
1290
1291 template <typename Fn, typename Mapper, size_t... InputI, size_t... OptionI>
1292 V8_INLINE auto ExplodeImpl(Fn fn, Mapper& mapper,
1293 std::index_sequence<InputI...>,
1294 std::index_sequence<OptionI...>) const {
1295 auto options = this->derived_this().options();
1296 USE(options);
1297 return fn(mapper.Map(this->input(InputI))...,
1298 std::get<OptionI>(options)...);
1299 }
1300
1301 template <typename Fn, typename Mapper>
1302 V8_INLINE auto Explode(Fn fn, Mapper& mapper) const {
1303 return ExplodeImpl(
1304 fn, mapper, std::make_index_sequence<input_count>(),
1305 std::make_index_sequence<
1306 std::tuple_size_v<decltype(this->derived_this().options())>>());
1307 }
1308};
1309
1310#define SUPPORTED_OPERATIONS_LIST(V) \
1311 V(float32_round_down, Float32RoundDown) \
1312 V(float64_round_down, Float64RoundDown) \
1313 V(float32_round_up, Float32RoundUp) \
1314 V(float64_round_up, Float64RoundUp) \
1315 V(float32_round_to_zero, Float32RoundTruncate) \
1316 V(float64_round_to_zero, Float64RoundTruncate) \
1317 V(float32_round_ties_even, Float32RoundTiesEven) \
1318 V(float64_round_ties_even, Float64RoundTiesEven) \
1319 V(float64_round_ties_away, Float64RoundTiesAway) \
1320 V(int32_div_is_safe, Int32DivIsSafe) \
1321 V(uint32_div_is_safe, Uint32DivIsSafe) \
1322 V(word32_shift_is_safe, Word32ShiftIsSafe) \
1323 V(word32_ctz, Word32Ctz) \
1324 V(word64_ctz, Word64Ctz) \
1325 V(word64_ctz_lowerable, Word64CtzLowerable) \
1326 V(word32_popcnt, Word32Popcnt) \
1327 V(word64_popcnt, Word64Popcnt) \
1328 V(word32_reverse_bits, Word32ReverseBits) \
1329 V(word64_reverse_bits, Word64ReverseBits) \
1330 V(float32_select, Float32Select) \
1331 V(float64_select, Float64Select) \
1332 V(int32_abs_with_overflow, Int32AbsWithOverflow) \
1333 V(int64_abs_with_overflow, Int64AbsWithOverflow) \
1334 V(word32_rol, Word32Rol) \
1335 V(word64_rol, Word64Rol) \
1336 V(word64_rol_lowerable, Word64RolLowerable) \
1337 V(sat_conversion_is_safe, SatConversionIsSafe) \
1338 V(word32_select, Word32Select) \
1339 V(word64_select, Word64Select) \
1340 V(float64_to_float16_raw_bits, Float16RawBitsConversion) \
1341 V(float16_raw_bits_to_float64, Float16RawBitsConversion) \
1342 V(float16, Float16)
1343
1345#define DECLARE_FIELD(name, machine_name) bool name##_;
1346#define DECLARE_GETTER(name, machine_name) \
1347 static bool name() { \
1348 if constexpr (DEBUG_BOOL) { \
1349 base::MutexGuard lock(mutex_.Pointer()); \
1350 DCHECK(initialized_); \
1351 } \
1352 return instance_.name##_; \
1353 }
1354
1355 public:
1356 static void Initialize();
1357 static bool IsUnalignedLoadSupported(MemoryRepresentation repr);
1358 static bool IsUnalignedStoreSupported(MemoryRepresentation repr);
1360
1361 private:
1363
1364 static bool initialized_;
1365 static base::LazyMutex mutex_;
1366 static SupportedOperations instance_;
1367
1368#undef DECLARE_FIELD
1369#undef DECLARE_GETTER
1370};
1371
1372template <RegisterRepresentation::Enum... reps>
1374 static constexpr std::array<RegisterRepresentation, sizeof...(reps)>
1375 rep_array{RegisterRepresentation{reps}...};
1376 return base::VectorOf(rep_array);
1377}
1378
1379template <MaybeRegisterRepresentation::Enum... reps>
1381 static constexpr std::array<MaybeRegisterRepresentation, sizeof...(reps)>
1382 rep_array{MaybeRegisterRepresentation{reps}...};
1383 return base::VectorOf(rep_array);
1384}
1385
1386#if DEBUG
1387V8_EXPORT_PRIVATE void ValidateOpInputRep(
1388 const Graph& graph, OpIndex input,
1389 std::initializer_list<RegisterRepresentation> expected_rep,
1390 const Operation* checked_op = nullptr,
1391 std::optional<size_t> projection_index = {});
1392V8_EXPORT_PRIVATE void ValidateOpInputRep(
1393 const Graph& graph, OpIndex input, RegisterRepresentation expected_rep,
1394 const Operation* checked_op = nullptr,
1395 std::optional<size_t> projection_index = {});
1396#endif // DEBUG
1397
1398// DeadOp is a special operation that can be used by analyzers to mark
1399// operations as being dead (typically, it should be used by calling the Graph's
1400// KillOperation method, which will Replace the old operation by a DeadOp).
1401// CopyingPhase and Analyzers should ignore Dead operations. A Dead operation
1402// should never be the input of a non-dead operation.
1403struct DeadOp : FixedArityOperationT<0, DeadOp> {
1404 static constexpr OpEffects effects = OpEffects();
1405
1407
1412
1413 auto options() const { return std::tuple{}; }
1414};
1415
1416struct AbortCSADcheckOp : FixedArityOperationT<1, AbortCSADcheckOp> {
1417 static constexpr OpEffects effects =
1419
1421
1424 return MaybeRepVector<MaybeRegisterRepresentation::Tagged()>();
1425 }
1426
1427 V<String> message() const { return Base::input<String>(0); }
1428
1429 explicit AbortCSADcheckOp(V<String> message) : Base(message) {}
1430
1431 auto options() const { return std::tuple{}; }
1432};
1433
1434struct GenericBinopOp : FixedArityOperationT<4, GenericBinopOp> {
1435#define GENERIC_BINOP_LIST(V) \
1436 V(Add) \
1437 V(Multiply) \
1438 V(Subtract) \
1439 V(Divide) \
1440 V(Modulus) \
1441 V(Exponentiate) \
1442 V(BitwiseAnd) \
1443 V(BitwiseOr) \
1444 V(BitwiseXor) \
1445 V(ShiftLeft) \
1446 V(ShiftRight) \
1447 V(ShiftRightLogical) \
1448 V(Equal) \
1449 V(StrictEqual) \
1450 V(LessThan) \
1451 V(LessThanOrEqual) \
1452 V(GreaterThan) \
1453 V(GreaterThanOrEqual)
1454 enum class Kind : uint8_t {
1455#define DEFINE_KIND(Name) k##Name,
1457#undef DEFINE_KIND
1458 };
1460
1461 static constexpr OpEffects effects = OpEffects().CanCallAnything();
1462
1463 THROWING_OP_BOILERPLATE(RegisterRepresentation::Tagged())
1464
1465 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
1466 ZoneVector<MaybeRegisterRepresentation>& storage) const {
1467 return MaybeRepVector<MaybeRegisterRepresentation::Tagged(),
1468 MaybeRegisterRepresentation::Tagged()>();
1469 }
1470
1471 V<Object> left() const { return input<Object>(0); }
1472 V<Object> right() const { return input<Object>(1); }
1473 V<FrameState> frame_state() const { return input<FrameState>(2); }
1474 V<Context> context() const { return input<Context>(3); }
1475
1477 V<Context> context, Kind kind,
1478 LazyDeoptOnThrow lazy_deopt_on_throw)
1479 : Base(left, right, frame_state, context),
1480 kind(kind),
1481 lazy_deopt_on_throw(lazy_deopt_on_throw) {}
1482
1483 auto options() const { return std::tuple{kind, lazy_deopt_on_throw}; }
1484};
1485V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
1487
1488struct GenericUnopOp : FixedArityOperationT<3, GenericUnopOp> {
1489#define GENERIC_UNOP_LIST(V) \
1490 V(BitwiseNot) \
1491 V(Negate) \
1492 V(Increment) \
1493 V(Decrement)
1494 enum class Kind : uint8_t {
1495#define DEFINE_KIND(Name) k##Name,
1497#undef DEFINE_KIND
1498 };
1500
1501 static constexpr OpEffects effects = OpEffects().CanCallAnything();
1502
1503 THROWING_OP_BOILERPLATE(RegisterRepresentation::Tagged())
1504
1505 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
1506 ZoneVector<MaybeRegisterRepresentation>& storage) const {
1507 return MaybeRepVector<MaybeRegisterRepresentation::Tagged()>();
1508 }
1509
1510 V<Object> input() const { return Base::input<Object>(0); }
1511 V<FrameState> frame_state() const { return Base::input<FrameState>(1); }
1512 V<Context> context() const { return Base::input<Context>(2); }
1513
1515 Kind kind, LazyDeoptOnThrow lazy_deopt_on_throw)
1516 : Base(input, frame_state, context),
1517 kind(kind),
1518 lazy_deopt_on_throw(lazy_deopt_on_throw) {}
1519
1520 auto options() const { return std::tuple{kind, lazy_deopt_on_throw}; }
1521};
1522V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
1524
1525struct ToNumberOrNumericOp : FixedArityOperationT<3, ToNumberOrNumericOp> {
1527
1528 static constexpr OpEffects effects = OpEffects().CanCallAnything();
1529
1530 THROWING_OP_BOILERPLATE(RegisterRepresentation::Tagged())
1531
1532 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
1533 ZoneVector<MaybeRegisterRepresentation>& storage) const {
1534 return MaybeRepVector<MaybeRegisterRepresentation::Tagged()>();
1535 }
1536
1537 V<Object> input() const { return Base::input<Object>(0); }
1538 V<FrameState> frame_state() const { return Base::input<FrameState>(1); }
1539 V<Context> context() const { return Base::input<Context>(2); }
1540
1543 LazyDeoptOnThrow lazy_deopt_on_throw)
1544 : Base(input, frame_state, context),
1545 kind(kind),
1546 lazy_deopt_on_throw(lazy_deopt_on_throw) {}
1547
1548 auto options() const { return std::tuple{kind, lazy_deopt_on_throw}; }
1549};
1550
1551// Word32SignHint is a type-hint used during Maglev->Turboshaft
1552// translation to avoid having multiple values being used as both Int32 and
1553// Uint32: for such cases, Maglev has explicit conversions, and it's helpful to
1554// also have them in Turboshaft. Eventually, Word32SignHint is just a
1555// nop in Turboshaft, since as far as Machine level graph is concerned, both
1556// Int32 and Uint32 are just Word32 registers.
1557struct Word32SignHintOp : FixedArityOperationT<1, Word32SignHintOp> {
1558 enum class Sign : bool { kSigned, kUnsigned };
1560
1561 static constexpr OpEffects effects = OpEffects();
1563 return RepVector<RegisterRepresentation::Word32()>();
1564 }
1565
1568 return MaybeRepVector<MaybeRegisterRepresentation::Word32()>();
1569 }
1570
1571 V<Word32> input() const { return Base::input<Word32>(0); }
1572
1573 Word32SignHintOp(V<Word32> input, Sign sign) : Base(input), sign(sign) {}
1574
1575 auto options() const { return std::tuple{sign}; }
1576};
1577V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
1579
1580struct WordBinopOp : FixedArityOperationT<2, WordBinopOp> {
1581 enum class Kind : uint8_t {
1582 kAdd,
1583 kMul,
1584 kSignedMulOverflownBits,
1585 kUnsignedMulOverflownBits,
1586 kBitwiseAnd,
1587 kBitwiseOr,
1588 kBitwiseXor,
1589 kSub,
1590 kSignedDiv,
1591 kUnsignedDiv,
1592 kSignedMod,
1593 kUnsignedMod,
1594 };
1597
1598 // We must avoid division by 0.
1599 static constexpr OpEffects effects = OpEffects().CanDependOnChecks();
1601 return base::VectorOf(static_cast<const RegisterRepresentation*>(&rep), 1);
1602 }
1603
1606 return InputsRepFactory::PairOf(rep);
1607 }
1608
1609 template <class WordType = Word>
1611 requires(IsWord<WordType>())
1612 {
1613 return input<WordType>(0);
1614 }
1615 template <class WordType = Word>
1617 requires(IsWord<WordType>())
1618 {
1619 return input<WordType>(1);
1620 }
1621
1622 bool IsCommutative() const { return IsCommutative(kind); }
1623
1624 static bool IsCommutative(Kind kind) {
1625 switch (kind) {
1626 case Kind::kAdd:
1627 case Kind::kMul:
1628 case Kind::kSignedMulOverflownBits:
1629 case Kind::kUnsignedMulOverflownBits:
1630 case Kind::kBitwiseAnd:
1631 case Kind::kBitwiseOr:
1632 case Kind::kBitwiseXor:
1633 return true;
1634 case Kind::kSub:
1635 case Kind::kSignedDiv:
1636 case Kind::kUnsignedDiv:
1637 case Kind::kSignedMod:
1638 case Kind::kUnsignedMod:
1639 return false;
1640 }
1641 }
1642
1643 static bool IsAssociative(Kind kind) {
1644 switch (kind) {
1645 case Kind::kAdd:
1646 case Kind::kMul:
1647 case Kind::kBitwiseAnd:
1648 case Kind::kBitwiseOr:
1649 case Kind::kBitwiseXor:
1650 return true;
1651 case Kind::kSignedMulOverflownBits:
1652 case Kind::kUnsignedMulOverflownBits:
1653 case Kind::kSub:
1654 case Kind::kSignedDiv:
1655 case Kind::kUnsignedDiv:
1656 case Kind::kSignedMod:
1657 case Kind::kUnsignedMod:
1658 return false;
1659 }
1660 }
1661 // The Word32 and Word64 versions of the operator compute the same result when
1662 // truncated to 32 bit.
1664 switch (kind) {
1665 case Kind::kAdd:
1666 case Kind::kMul:
1667 case Kind::kBitwiseAnd:
1668 case Kind::kBitwiseOr:
1669 case Kind::kBitwiseXor:
1670 case Kind::kSub:
1671 return true;
1672 case Kind::kSignedMulOverflownBits:
1673 case Kind::kUnsignedMulOverflownBits:
1674 case Kind::kSignedDiv:
1675 case Kind::kUnsignedDiv:
1676 case Kind::kSignedMod:
1677 case Kind::kUnsignedMod:
1678 return false;
1679 }
1680 }
1681
1683 : Base(left, right), kind(kind), rep(rep) {}
1684
1685 auto options() const { return std::tuple{kind, rep}; }
1686 void PrintOptions(std::ostream& os) const;
1687};
1688
1689struct FloatBinopOp : FixedArityOperationT<2, FloatBinopOp> {
1690 enum class Kind : uint8_t {
1691 kAdd,
1692 kMul,
1693 kMin,
1694 kMax,
1695 kSub,
1696 kDiv,
1697 kMod,
1698 kPower,
1699 kAtan2,
1700 };
1703
1704 static constexpr OpEffects effects = OpEffects();
1706 return base::VectorOf(static_cast<const RegisterRepresentation*>(&rep), 1);
1707 }
1708
1711 return InputsRepFactory::PairOf(rep);
1712 }
1713
1714 V<Float> left() const { return input<Float>(0); }
1715 V<Float> right() const { return input<Float>(1); }
1716
1717 static bool IsCommutative(Kind kind) {
1718 switch (kind) {
1719 case Kind::kAdd:
1720 case Kind::kMul:
1721 case Kind::kMin:
1722 case Kind::kMax:
1723 return true;
1724 case Kind::kSub:
1725 case Kind::kDiv:
1726 case Kind::kMod:
1727 case Kind::kPower:
1728 case Kind::kAtan2:
1729 return false;
1730 }
1731 }
1732
1735 : Base(left, right), kind(kind), rep(rep) {}
1736
1737 void Validate(const Graph& graph) const {
1738 DCHECK_IMPLIES(kind == any_of(Kind::kPower, Kind::kAtan2, Kind::kMod),
1739 rep == FloatRepresentation::Float64());
1740 }
1741 auto options() const { return std::tuple{kind, rep}; }
1742 void PrintOptions(std::ostream& os) const;
1743};
1744
1745struct Word32PairBinopOp : FixedArityOperationT<4, Word32PairBinopOp> {
1746 enum class Kind : uint8_t {
1747 kAdd,
1748 kSub,
1749 kMul,
1750 kShiftLeft,
1751 kShiftRightArithmetic,
1752 kShiftRightLogical,
1753 };
1755
1756 static constexpr OpEffects effects = OpEffects();
1757
1759 return RepVector<RegisterRepresentation::Word32(),
1760 RegisterRepresentation::Word32()>();
1761 }
1762
1764 const ZoneVector<MaybeRegisterRepresentation>& storage) const {
1765 return MaybeRepVector<MaybeRegisterRepresentation::Word32(),
1766 MaybeRegisterRepresentation::Word32(),
1767 MaybeRegisterRepresentation::Word32(),
1768 MaybeRegisterRepresentation::Word32()>();
1769 }
1770
1771 V<Word32> left_low() const { return input<Word32>(0); }
1772 V<Word32> left_high() const { return input<Word32>(1); }
1773 V<Word32> right_low() const { return input<Word32>(2); }
1774 V<Word32> right_high() const { return input<Word32>(3); }
1775
1777 V<Word32> right_low, V<Word32> right_high, Kind kind)
1778 : Base(left_low, left_high, right_low, right_high), kind(kind) {}
1779
1780 auto options() const { return std::tuple{kind}; }
1781 void PrintOptions(std::ostream& os) const;
1782};
1783
1785 : FixedArityOperationT<3, WordBinopDeoptOnOverflowOp> {
1786 enum class Kind : uint8_t {
1787 kSignedAdd,
1788 kSignedMul,
1789 kSignedSub,
1790 kSignedDiv,
1791 kSignedMod,
1792 kUnsignedDiv,
1793 kUnsignedMod
1794 };
1799
1800 static constexpr OpEffects effects = OpEffects().CanDeopt();
1802 return base::VectorOf(static_cast<const RegisterRepresentation*>(&rep), 1);
1803 }
1804
1807 return InputsRepFactory::PairOf(rep);
1808 }
1809
1810 V<Word> left() const { return input<Word>(0); }
1811 V<Word> right() const { return input<Word>(1); }
1812 V<FrameState> frame_state() const { return input<FrameState>(2); }
1813
1815 V<FrameState> frame_state, Kind kind,
1816 WordRepresentation rep, FeedbackSource feedback,
1818 : Base(left, right, frame_state),
1819 kind(kind),
1820 rep(rep),
1821 feedback(feedback),
1822 mode(mode) {}
1823
1824 void Validate(const Graph& graph) const {
1825 DCHECK_IMPLIES(kind == Kind::kUnsignedDiv || kind == Kind::kUnsignedMod,
1826 rep == WordRepresentation::Word32());
1827 }
1828 auto options() const { return std::tuple{kind, rep, feedback, mode}; }
1829 void PrintOptions(std::ostream& os) const;
1830};
1831
1833 : FixedArityOperationT<2, OverflowCheckedBinopOp> {
1834 static constexpr int kValueIndex = 0;
1835 static constexpr int kOverflowIndex = 1;
1836
1837 enum class Kind : uint8_t {
1838 kSignedAdd,
1839 kSignedMul,
1840 kSignedSub,
1841 };
1844
1845 static constexpr OpEffects effects = OpEffects();
1847 switch (rep.value()) {
1848 case WordRepresentation::Word32():
1849 return RepVector<RegisterRepresentation::Word32(),
1850 RegisterRepresentation::Word32()>();
1851 case WordRepresentation::Word64():
1852 return RepVector<RegisterRepresentation::Word64(),
1853 RegisterRepresentation::Word32()>();
1854 }
1855 }
1856
1859 return InputsRepFactory::PairOf(rep);
1860 }
1861
1862 V<Word> left() const { return input<Word>(0); }
1863 V<Word> right() const { return input<Word>(1); }
1864
1865 static bool IsCommutative(Kind kind) {
1866 switch (kind) {
1867 case Kind::kSignedAdd:
1868 case Kind::kSignedMul:
1869 return true;
1870 case Kind::kSignedSub:
1871 return false;
1872 }
1873 }
1874
1877 : Base(left, right), kind(kind), rep(rep) {}
1878
1879 auto options() const { return std::tuple{kind, rep}; }
1880 void PrintOptions(std::ostream& os) const;
1881};
1882
1883struct WordUnaryOp : FixedArityOperationT<1, WordUnaryOp> {
1884 enum class Kind : uint8_t {
1885 kReverseBytes,
1886 kCountLeadingZeros,
1887 kCountTrailingZeros,
1888 kPopCount,
1889 kSignExtend8,
1890 kSignExtend16,
1891 };
1894 static constexpr OpEffects effects = OpEffects();
1896 return base::VectorOf(static_cast<const RegisterRepresentation*>(&rep), 1);
1897 }
1898
1901 return InputsRepFactory::SingleRep(rep);
1902 }
1903
1904 V<Word> input() const { return Base::input<Word>(0); }
1905
1906 V8_EXPORT_PRIVATE static bool IsSupported(Kind kind, WordRepresentation rep);
1907
1909 : Base(input), kind(kind), rep(rep) {}
1910
1911 auto options() const { return std::tuple{kind, rep}; }
1912};
1913V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
1915
1917 : FixedArityOperationT<1, OverflowCheckedUnaryOp> {
1918 static constexpr int kValueIndex = 0;
1919 static constexpr int kOverflowIndex = 1;
1920
1921 enum class Kind : uint8_t { kAbs };
1924 static constexpr OpEffects effects = OpEffects();
1926 switch (rep.value()) {
1927 case WordRepresentation::Word32():
1928 return RepVector<RegisterRepresentation::Word32(),
1929 RegisterRepresentation::Word32()>();
1930 case WordRepresentation::Word64():
1931 return RepVector<RegisterRepresentation::Word64(),
1932 RegisterRepresentation::Word32()>();
1933 }
1934 }
1935
1938 return InputsRepFactory::SingleRep(rep);
1939 }
1940
1941 V<Word> input() const { return Base::input<Word>(0); }
1942
1945 : Base(input), kind(kind), rep(rep) {}
1946
1947 auto options() const { return std::tuple{kind, rep}; }
1948};
1949V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
1951
1952struct FloatUnaryOp : FixedArityOperationT<1, FloatUnaryOp> {
1953 enum class Kind : uint8_t {
1954 kAbs,
1955 kNegate,
1956 kSilenceNaN,
1957 kRoundDown, // round towards -infinity
1958 kRoundUp, // round towards +infinity
1959 kRoundToZero, // round towards 0
1960 kRoundTiesEven, // break ties by rounding towards the next even number
1961 kLog,
1962 kLog2,
1963 kLog10,
1964 kLog1p,
1965 kSqrt,
1966 kCbrt,
1967 kExp,
1968 kExpm1,
1969 kSin,
1970 kCos,
1971 kSinh,
1972 kCosh,
1973 kAcos,
1974 kAsin,
1975 kAsinh,
1976 kAcosh,
1977 kTan,
1978 kTanh,
1979 kAtan,
1980 kAtanh,
1981 };
1982
1985
1986 static constexpr OpEffects effects = OpEffects();
1988 return base::VectorOf(static_cast<const RegisterRepresentation*>(&rep), 1);
1989 }
1990
1993 return InputsRepFactory::SingleRep(rep);
1994 }
1995
1996 V<Float> input() const { return Base::input<Float>(0); }
1997
1998 V8_EXPORT_PRIVATE static bool IsSupported(Kind kind, FloatRepresentation rep);
1999
2001 : Base(input), kind(kind), rep(rep) {}
2002
2003 auto options() const { return std::tuple{kind, rep}; }
2004};
2005V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
2007
2008struct ShiftOp : FixedArityOperationT<2, ShiftOp> {
2009 enum class Kind : uint8_t {
2010 kShiftRightArithmeticShiftOutZeros,
2011 kShiftRightArithmetic,
2012 kShiftRightLogical,
2013 kShiftLeft,
2014 kRotateRight,
2015 kRotateLeft
2016 };
2019
2020 static constexpr OpEffects effects = OpEffects();
2022 return base::VectorOf(static_cast<const RegisterRepresentation*>(&rep), 1);
2023 }
2024
2027 return InitVectorOf(storage,
2028 {static_cast<const RegisterRepresentation&>(rep),
2029 RegisterRepresentation::Word32()});
2030 }
2031
2032 template <typename WordT = Word>
2033 requires(IsWord<WordT>())
2034 V<WordT> left() const {
2035 DCHECK(IsValidTypeFor<WordT>(rep));
2036 return input<WordT>(0);
2037 }
2038 V<Word32> right() const { return input<Word32>(1); }
2039
2040 bool IsRightShift() const { return IsRightShift(kind); }
2041
2042 static bool IsRightShift(Kind kind) {
2043 switch (kind) {
2044 case Kind::kShiftRightArithmeticShiftOutZeros:
2045 case Kind::kShiftRightArithmetic:
2046 case Kind::kShiftRightLogical:
2047 return true;
2048 case Kind::kShiftLeft:
2049 case Kind::kRotateRight:
2050 case Kind::kRotateLeft:
2051 return false;
2052 }
2053 }
2054 // The Word32 and Word64 versions of the operator compute the same result when
2055 // truncated to 32 bit.
2057 switch (kind) {
2058 case Kind::kShiftLeft:
2059 return true;
2060 case Kind::kShiftRightArithmeticShiftOutZeros:
2061 case Kind::kShiftRightArithmetic:
2062 case Kind::kShiftRightLogical:
2063 case Kind::kRotateRight:
2064 case Kind::kRotateLeft:
2065 return false;
2066 }
2067 }
2068
2070 : Base(left, right), kind(kind), rep(rep) {}
2071
2072 auto options() const { return std::tuple{kind, rep}; }
2073};
2074V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
2075 ShiftOp::Kind kind);
2076
2077struct ComparisonOp : FixedArityOperationT<2, ComparisonOp> {
2078 enum class Kind : uint8_t {
2079 kEqual,
2084 };
2087
2088 static constexpr OpEffects effects = OpEffects();
2090 return RepVector<RegisterRepresentation::Word32()>();
2091 }
2092
2095 return InputsRepFactory::PairOf(rep);
2096 }
2097
2098 static bool IsCommutative(Kind kind) { return kind == Kind::kEqual; }
2099
2100 template <typename T = Any>
2101 V<T> left() const {
2102 DCHECK(IsValidTypeFor<T>(rep));
2103 return input<T>(0);
2104 }
2105 template <typename T = Any>
2106 V<T> right() const {
2107 DCHECK(IsValidTypeFor<T>(rep));
2108 return input<T>(1);
2109 }
2110
2112 : Base(left, right), kind(kind), rep(rep) {}
2113
2114 void Validate(const Graph& graph) const {
2115 if (kind == Kind::kEqual) {
2116 DCHECK(rep == any_of(RegisterRepresentation::Word32(),
2117 RegisterRepresentation::Word64(),
2118 RegisterRepresentation::Float32(),
2119 RegisterRepresentation::Float64(),
2120 RegisterRepresentation::Tagged()));
2121
2122 RegisterRepresentation input_rep = rep;
2123#ifdef V8_COMPRESS_POINTERS
2124 // In the presence of pointer compression, we only compare the lower
2125 // 32bit.
2126 if (input_rep == RegisterRepresentation::Tagged()) {
2127 input_rep = RegisterRepresentation::Compressed();
2128 }
2129#endif // V8_COMPRESS_POINTERS
2130#ifdef DEBUG
2131 ValidateOpInputRep(graph, left(), input_rep);
2132 ValidateOpInputRep(graph, right(), input_rep);
2133#endif // DEBUG
2134 USE(input_rep);
2135 } else {
2136 DCHECK_EQ(rep, any_of(RegisterRepresentation::Word32(),
2137 RegisterRepresentation::Word64(),
2138 RegisterRepresentation::Float32(),
2139 RegisterRepresentation::Float64()));
2141 rep == any_of(RegisterRepresentation::Float32(),
2142 RegisterRepresentation::Float64()),
2143 kind == any_of(Kind::kSignedLessThan, Kind::kSignedLessThanOrEqual));
2144 }
2145 }
2146 auto options() const { return std::tuple{kind, rep}; }
2147};
2148V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
2151
2153 enum class Kind : uint8_t {
2154 // convert between different floating-point types. Note that the
2155 // Float64->Float32 conversion is truncating.
2156 kFloatConversion,
2157 // overflow guaranteed to result in the minimal integer
2158 kSignedFloatTruncateOverflowToMin,
2159 kUnsignedFloatTruncateOverflowToMin,
2160 // JS semantics float64 to word32 truncation
2161 // https://tc39.es/ecma262/#sec-touint32
2162 kJSFloatTruncate,
2163 // convert float64 to float16, then bitcast word32. Used for storing into
2164 // Float16Array and Math.fround16.
2165 kJSFloat16TruncateWithBitcast,
2166 // bitcast word32 to float16 and convert to float64. Used for loading from
2167 // Float16Array and Math.fround16.
2168 kJSFloat16ChangeWithBitcast,
2169 // convert (un)signed integer to floating-point value
2170 kSignedToFloat,
2171 kUnsignedToFloat,
2172 // extract half of a float64 value
2173 kExtractHighHalf,
2174 kExtractLowHalf,
2175 // increase bit-width for unsigned integer values
2176 kZeroExtend,
2177 // increase bid-width for signed integer values
2178 kSignExtend,
2179 // truncate word64 to word32
2180 kTruncate,
2181 // preserve bits, change meaning
2182 kBitcast
2183 };
2184 // Violated assumptions result in undefined behavior.
2185 enum class Assumption : uint8_t {
2186 kNoAssumption,
2187 // Used for conversions from floating-point to integer, assumes that the
2188 // value doesn't exceed the integer range.
2190 // Assume that the original value can be recovered by a corresponding
2191 // reverse transformation.
2192 kReversible,
2193 };
2195 // Reversible means undefined behavior if value cannot be represented
2196 // precisely.
2200
2201 // Returns true if change<kind>(change<reverse_kind>(a)) == a for all a.
2202 // This assumes that change<reverse_kind> uses the inverted {from} and {to}
2203 // representations, i.e. the input to the inner change op has the same
2204 // representation as the result of the outer change op.
2205 static bool IsReversible(Kind kind, Assumption assumption,
2207 RegisterRepresentation to, Kind reverse_kind,
2208 bool signalling_nan_possible) {
2209 switch (kind) {
2210 case Kind::kFloatConversion:
2211 return from == RegisterRepresentation::Float32() &&
2212 to == RegisterRepresentation::Float64() &&
2213 reverse_kind == Kind::kFloatConversion &&
2214 !signalling_nan_possible;
2215 case Kind::kSignedFloatTruncateOverflowToMin:
2216 return assumption == Assumption::kReversible &&
2217 reverse_kind == Kind::kSignedToFloat;
2218 case Kind::kUnsignedFloatTruncateOverflowToMin:
2219 return assumption == Assumption::kReversible &&
2220 reverse_kind == Kind::kUnsignedToFloat;
2221 case Kind::kJSFloatTruncate:
2222 return false;
2223 case Kind::kJSFloat16TruncateWithBitcast:
2224 case Kind::kJSFloat16ChangeWithBitcast:
2225 return false;
2226 case Kind::kSignedToFloat:
2227 if (from == RegisterRepresentation::Word32() &&
2228 to == RegisterRepresentation::Float64()) {
2229 return reverse_kind == any_of(Kind::kSignedFloatTruncateOverflowToMin,
2230 Kind::kJSFloatTruncate);
2231 } else {
2232 return assumption == Assumption::kReversible &&
2233 reverse_kind ==
2234 any_of(Kind::kSignedFloatTruncateOverflowToMin);
2235 }
2236 case Kind::kUnsignedToFloat:
2237 if (from == RegisterRepresentation::Word32() &&
2238 to == RegisterRepresentation::Float64()) {
2239 return reverse_kind ==
2240 any_of(Kind::kUnsignedFloatTruncateOverflowToMin,
2241 Kind::kJSFloatTruncate);
2242 } else {
2243 return assumption == Assumption::kReversible &&
2244 reverse_kind == Kind::kUnsignedFloatTruncateOverflowToMin;
2245 }
2246 case Kind::kExtractHighHalf:
2247 case Kind::kExtractLowHalf:
2248 return false;
2249 case Kind::kZeroExtend:
2250 case Kind::kSignExtend:
2251 DCHECK_EQ(from, RegisterRepresentation::Word32());
2252 DCHECK_EQ(to, RegisterRepresentation::Word64());
2253 return reverse_kind == Kind::kTruncate;
2254 case Kind::kTruncate:
2255 DCHECK_EQ(from, RegisterRepresentation::Word64());
2256 DCHECK_EQ(to, RegisterRepresentation::Word32());
2257 return reverse_kind == Kind::kBitcast;
2258 case Kind::kBitcast:
2259 return reverse_kind == Kind::kBitcast;
2260 }
2261 }
2262
2263 bool IsReversibleBy(Kind reverse_kind, bool signalling_nan_possible) const {
2264 return IsReversible(kind, assumption, from, to, reverse_kind,
2265 signalling_nan_possible);
2266 }
2267
2268 static constexpr OpEffects effects = OpEffects();
2270 return base::VectorOf(&to, 1);
2271 }
2272
2275 return InputsRepFactory::SingleRep(from);
2276 }
2277
2278 template <typename Type = Untagged>
2279 requires IsUntagged<Type>
2280 V<Type> input() const {
2281 DCHECK(IsValidTypeFor<Type>(from));
2282 return Base::input<Type>(0);
2283 }
2284
2287 : Base(input), kind(kind), assumption(assumption), from(from), to(to) {}
2288
2289 void Validate(const Graph& graph) const {
2290 DCHECK_NE(from, RegisterRepresentation::Tagged());
2291 DCHECK_NE(to, RegisterRepresentation::Tagged());
2292 // Bitcasts from and to Tagged should use a TaggedBitcast instead (which has
2293 // different effects, since it's unsafe to reorder such bitcasts accross
2294 // GCs).
2295 }
2296 auto options() const { return std::tuple{kind, assumption, from, to}; }
2297};
2298V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
2300V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
2301 ChangeOp::Assumption assumption);
2304
2306 enum class Kind : uint8_t {
2307 kUint32ToInt32,
2308 kInt64ToInt32,
2309 kUint64ToInt32,
2310 kUint64ToInt64,
2311 kFloat64ToInt32,
2312 kFloat64ToUint32,
2313 kFloat64ToAdditiveSafeInteger,
2314 kFloat64ToInt64,
2315 kFloat64NotHole,
2316 };
2320
2321 static constexpr OpEffects effects = OpEffects().CanDeopt();
2323 switch (kind) {
2324 case Kind::kUint32ToInt32:
2325 case Kind::kInt64ToInt32:
2326 case Kind::kUint64ToInt32:
2327 case Kind::kFloat64ToInt32:
2328 case Kind::kFloat64ToUint32:
2329 return RepVector<RegisterRepresentation::Word32()>();
2330 case Kind::kUint64ToInt64:
2331 case Kind::kFloat64ToAdditiveSafeInteger:
2332 case Kind::kFloat64ToInt64:
2333 return RepVector<RegisterRepresentation::Word64()>();
2334 case Kind::kFloat64NotHole:
2335 return RepVector<RegisterRepresentation::Float64()>();
2336 }
2337 }
2338
2341 switch (kind) {
2342 case Kind::kUint32ToInt32:
2343 return MaybeRepVector<MaybeRegisterRepresentation::Word32()>();
2344 case Kind::kInt64ToInt32:
2345 case Kind::kUint64ToInt32:
2346 case Kind::kUint64ToInt64:
2347 return MaybeRepVector<MaybeRegisterRepresentation::Word64()>();
2348 case Kind::kFloat64ToInt32:
2349 case Kind::kFloat64ToUint32:
2350 case Kind::kFloat64ToAdditiveSafeInteger:
2351 case Kind::kFloat64ToInt64:
2352 case Kind::kFloat64NotHole:
2353 return MaybeRepVector<MaybeRegisterRepresentation::Float64()>();
2354 }
2355 }
2356
2357 V<Untagged> input() const { return Base::input<Untagged>(0); }
2358 V<FrameState> frame_state() const { return Base::input<FrameState>(1); }
2359
2361 CheckForMinusZeroMode minus_zero_mode,
2362 const FeedbackSource& feedback)
2363 : Base(input, frame_state),
2364 kind(kind),
2365 minus_zero_mode(minus_zero_mode),
2366 feedback(feedback) {}
2367
2368 void Validate(const Graph& graph) const {
2369 DCHECK(Get(graph, frame_state()).Is<FrameStateOp>());
2370 }
2371
2372 auto options() const { return std::tuple{kind, minus_zero_mode, feedback}; }
2373};
2374V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
2376
2377// Perform a conversion and return a pair of the result and a bit if it was
2378// successful.
2379struct TryChangeOp : FixedArityOperationT<1, TryChangeOp> {
2380 static constexpr uint32_t kSuccessValue = 1;
2381 static constexpr uint32_t kFailureValue = 0;
2382 enum class Kind : uint8_t {
2383 // The result of the truncation is undefined if the result is out of range.
2384 kSignedFloatTruncateOverflowUndefined,
2385 kUnsignedFloatTruncateOverflowUndefined,
2386 };
2390
2391 static constexpr OpEffects effects = OpEffects();
2393 switch (to.value()) {
2394 case WordRepresentation::Word32():
2395 return RepVector<RegisterRepresentation::Word32(),
2396 RegisterRepresentation::Word32()>();
2397 case WordRepresentation::Word64():
2398 return RepVector<RegisterRepresentation::Word64(),
2399 RegisterRepresentation::Word32()>();
2400 }
2401 }
2402
2405 return InputsRepFactory::SingleRep(from);
2406 }
2407
2408 OpIndex input() const { return Base::input(0); }
2409
2412 : Base(input), kind(kind), from(from), to(to) {}
2413
2414 auto options() const { return std::tuple{kind, from, to}; }
2415};
2416V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
2418
2420 : FixedArityOperationT<2, BitcastWord32PairToFloat64Op> {
2421 static constexpr OpEffects effects = OpEffects();
2423 return RepVector<RegisterRepresentation::Float64()>();
2424 }
2425
2428 return MaybeRepVector<MaybeRegisterRepresentation::Word32(),
2429 MaybeRegisterRepresentation::Word32()>();
2430 }
2431
2432 V<Word32> high_word32() const { return input<Word32>(0); }
2433 V<Word32> low_word32() const { return input<Word32>(1); }
2434
2436 : Base(high_word32, low_word32) {}
2437
2438 auto options() const { return std::tuple{}; }
2439};
2440
2441struct TaggedBitcastOp : FixedArityOperationT<1, TaggedBitcastOp> {
2442 enum class Kind : uint8_t {
2443 kSmi, // This is a bitcast from a Word to a Smi or from a Smi to a Word
2444 kHeapObject, // This is a bitcast from or to a Heap Object
2445 kTagAndSmiBits, // This is a bitcast where only access to the tag and the
2446 // smi bits (if it's a smi) are valid
2447 kAny
2448 };
2452
2454 switch (kind) {
2455 case Kind::kSmi:
2456 case Kind::kTagAndSmiBits:
2457 return OpEffects();
2458 case Kind::kHeapObject:
2459 case Kind::kAny:
2460 // Due to moving GC, converting from or to pointers doesn't commute with
2461 // GC.
2462 return OpEffects().CanDoRawHeapAccess();
2463 }
2464 }
2465
2467 return base::VectorOf(&to, 1);
2468 }
2469
2472 return InputsRepFactory::SingleRep(from);
2473 }
2474
2475 OpIndex input() const { return Base::input(0); }
2476
2479 : Base(input), kind(kind), from(from), to(to) {}
2480
2481 void Validate(const Graph& graph) const {
2482 if (kind == Kind::kSmi) {
2483 DCHECK((from.IsWord() && to.IsTaggedOrCompressed()) ||
2484 (from.IsTaggedOrCompressed() && to.IsWord()));
2485 DCHECK_IMPLIES(from == RegisterRepresentation::Word64() ||
2486 to == RegisterRepresentation::Word64(),
2487 Is64());
2488 } else {
2489 // TODO(nicohartmann@): Without implicit truncation, the first case might
2490 // not be correct anymore.
2491 DCHECK((from.IsWord() && to == RegisterRepresentation::Tagged()) ||
2492 (from == RegisterRepresentation::Tagged() &&
2493 to == RegisterRepresentation::WordPtr()) ||
2494 (from == RegisterRepresentation::Compressed() &&
2495 to == RegisterRepresentation::Word32()));
2496 }
2497 }
2498 auto options() const { return std::tuple{from, to, kind}; }
2499};
2500std::ostream& operator<<(std::ostream& os, TaggedBitcastOp::Kind assumption);
2501
2502struct SelectOp : FixedArityOperationT<3, SelectOp> {
2503 enum class Implementation : uint8_t { kBranch, kCMove };
2504
2508
2509 static constexpr OpEffects effects = OpEffects();
2511 return base::VectorOf(&rep, 1);
2512 }
2513
2516 return InitVectorOf(storage, {RegisterRepresentation::Word32(), rep, rep});
2517 }
2518
2519 SelectOp(V<Word32> cond, V<Any> vtrue, V<Any> vfalse,
2521 : Base(cond, vtrue, vfalse), rep(rep), hint(hint), implem(implem) {}
2522
2523 void Validate(const Graph& graph) const {
2524 DCHECK_IMPLIES(implem == Implementation::kCMove,
2525 (rep == RegisterRepresentation::Word32() &&
2526 SupportedOperations::word32_select()) ||
2527 (rep == RegisterRepresentation::Word64() &&
2528 SupportedOperations::word64_select()) ||
2529 (rep == RegisterRepresentation::Float32() &&
2530 SupportedOperations::float32_select()) ||
2531 (rep == RegisterRepresentation::Float64() &&
2532 SupportedOperations::float64_select()));
2533 }
2534
2535 V<Word32> cond() const { return input<Word32>(0); }
2536 V<Any> vtrue() const { return input<Any>(1); }
2537 V<Any> vfalse() const { return input<Any>(2); }
2538
2539 auto options() const { return std::tuple{rep, hint, implem}; }
2540};
2541V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
2543
2544struct PhiOp : OperationT<PhiOp> {
2546
2547 // Phis have to remain at the beginning of the current block. As effects
2548 // cannot express this completely, we just mark them as having no effects but
2549 // treat them specially when scheduling operations.
2550 static constexpr OpEffects effects = OpEffects();
2552 return base::VectorOf(&rep, 1);
2553 }
2554
2557 storage.resize(input_count);
2558 for (size_t i = 0; i < input_count; ++i) {
2559 storage[i] = rep;
2560 }
2561 return base::VectorOf(storage);
2562 }
2563
2564 static constexpr size_t kLoopPhiBackEdgeIndex = 1;
2565
2567 : Base(inputs), rep(rep) {}
2568
2569 template <typename Fn, typename Mapper>
2570 V8_INLINE auto Explode(Fn fn, Mapper& mapper) const {
2571 auto mapped_inputs = mapper.template Map<64>(inputs());
2572 return fn(base::VectorOf(mapped_inputs), rep);
2573 }
2574
2575 void Validate(const Graph& graph) const { DCHECK_GT(input_count, 0); }
2576 auto options() const { return std::tuple{rep}; }
2577};
2578
2579// Used as a placeholder for a loop-phi while building the graph, replaced with
2580// a normal `PhiOp` before graph building is over, so it should never appear in
2581// a complete graph.
2582struct PendingLoopPhiOp : FixedArityOperationT<1, PendingLoopPhiOp> {
2584
2585 static constexpr OpEffects effects = OpEffects();
2587 return base::VectorOf(&rep, 1);
2588 }
2589
2592 return InputsRepFactory::SingleRep(rep);
2593 }
2594
2595 OpIndex first() const { return input(0); }
2597 : Base(first), rep(rep) {}
2598
2599 auto options() const { return std::tuple{rep}; }
2600};
2601
2602struct ConstantOp : FixedArityOperationT<0, ConstantOp> {
2603 enum class Kind : uint8_t {
2604 kWord32,
2605 kWord64,
2606 kFloat32,
2607 kFloat64,
2608 kSmi,
2609 kNumber, // TODO(tebbi): See if we can avoid number constants.
2610 kTaggedIndex,
2611 kExternal,
2613 kCompressedHeapObject,
2614 kTrustedHeapObject,
2615 kRelocatableWasmCall,
2616 kRelocatableWasmStubCall,
2617 kRelocatableWasmIndirectCallTarget,
2618 kRelocatableWasmCanonicalSignatureId
2619 };
2620
2623 union Storage {
2624 uint64_t integral;
2629
2630 Storage(uint64_t integral = 0) : integral(integral) {}
2631 Storage(i::Tagged<Smi> smi) : integral(smi.ptr()) {}
2632 Storage(i::Float64 constant) : float64(constant) {}
2633 Storage(i::Float32 constant) : float32(constant) {}
2634 Storage(ExternalReference constant) : external(constant) {}
2636
2637 inline bool operator==(const ConstantOp::Storage&) const {
2638 // It is tricky to implement this properly. We currently need to define
2639 // this for the matchers, but this should never be called.
2640 UNREACHABLE();
2641 }
2642 } storage;
2643
2644 static constexpr OpEffects effects = OpEffects();
2646 return base::VectorOf(&rep, 1);
2647 }
2648
2653
2655 switch (kind) {
2656 case Kind::kRelocatableWasmCanonicalSignatureId:
2657 case Kind::kWord32:
2658 return RegisterRepresentation::Word32();
2659 case Kind::kWord64:
2660 return RegisterRepresentation::Word64();
2661 case Kind::kFloat32:
2662 return RegisterRepresentation::Float32();
2663 case Kind::kFloat64:
2664 return RegisterRepresentation::Float64();
2665 case Kind::kExternal:
2666 case Kind::kTaggedIndex:
2667 case Kind::kTrustedHeapObject:
2668 case Kind::kRelocatableWasmCall:
2669 case Kind::kRelocatableWasmStubCall:
2670 return RegisterRepresentation::WordPtr();
2671 case Kind::kRelocatableWasmIndirectCallTarget:
2672 return RegisterRepresentation::Word32();
2673 case Kind::kSmi:
2674 case Kind::kHeapObject:
2675 case Kind::kNumber:
2676 return RegisterRepresentation::Tagged();
2677 case Kind::kCompressedHeapObject:
2678 return RegisterRepresentation::Compressed();
2679 }
2680 }
2681
2683 : Base(), kind(kind), storage(storage) {}
2684
2685 void Validate(const Graph& graph) const {
2687 kind == Kind::kWord32,
2688 storage.integral <= WordRepresentation::Word32().MaxUnsignedValue());
2690 kind == Kind::kRelocatableWasmCanonicalSignatureId,
2691 storage.integral <= WordRepresentation::Word32().MaxSignedValue());
2692 }
2693
2694 uint64_t integral() const {
2695 DCHECK(IsIntegral());
2696 return storage.integral;
2697 }
2698
2699 int64_t signed_integral() const {
2700 DCHECK(IsIntegral());
2701 switch (kind) {
2702 case Kind::kWord32:
2703 case Kind::kRelocatableWasmCanonicalSignatureId:
2704 return static_cast<int32_t>(storage.integral);
2705 case Kind::kWord64:
2706 return static_cast<int64_t>(storage.integral);
2707 default:
2708 UNREACHABLE();
2709 }
2710 }
2711
2712 uint32_t word32() const {
2713 DCHECK(kind == Kind::kWord32 || kind == Kind::kWord64);
2714 return static_cast<uint32_t>(storage.integral);
2715 }
2716
2717 uint64_t word64() const {
2718 DCHECK_EQ(kind, Kind::kWord64);
2719 return static_cast<uint64_t>(storage.integral);
2720 }
2721
2723 DCHECK_EQ(kind, Kind::kSmi);
2724 return i::Tagged<Smi>(storage.integral);
2725 }
2726
2728 DCHECK_EQ(kind, Kind::kNumber);
2729 return storage.float64;
2730 }
2731
2733 DCHECK_EQ(kind, Kind::kFloat32);
2734 return storage.float32;
2735 }
2736
2738 DCHECK_EQ(kind, Kind::kFloat64);
2739 return storage.float64;
2740 }
2741
2742 int32_t tagged_index() const {
2743 DCHECK_EQ(kind, Kind::kTaggedIndex);
2744 return static_cast<int32_t>(static_cast<uint32_t>(storage.integral));
2745 }
2746
2748 DCHECK_EQ(kind, Kind::kExternal);
2749 return storage.external;
2750 }
2751
2753 DCHECK(kind == Kind::kHeapObject || kind == Kind::kCompressedHeapObject ||
2754 kind == Kind::kTrustedHeapObject);
2755 return storage.handle;
2756 }
2757
2758 bool IsWord(uint64_t value) const {
2759 switch (kind) {
2760 case Kind::kWord32:
2761 return static_cast<uint32_t>(value) == word32();
2762 case Kind::kWord64:
2763 return value == word64();
2764 default:
2765 UNREACHABLE();
2766 }
2767 }
2768
2769 bool IsIntegral() const {
2770 return kind == Kind::kWord32 || kind == Kind::kWord64 ||
2771 kind == Kind::kRelocatableWasmCall ||
2772 kind == Kind::kRelocatableWasmStubCall ||
2773 kind == Kind::kRelocatableWasmCanonicalSignatureId ||
2774 kind == Kind::kRelocatableWasmIndirectCallTarget;
2775 }
2776
2777 auto options() const { return std::tuple{kind, storage}; }
2778
2779 void PrintOptions(std::ostream& os) const;
2781 HashingStrategy strategy = HashingStrategy::kDefault) const {
2782 switch (kind) {
2783 case Kind::kWord32:
2784 case Kind::kWord64:
2785 case Kind::kSmi:
2786 case Kind::kTaggedIndex:
2787 case Kind::kRelocatableWasmCall:
2788 case Kind::kRelocatableWasmStubCall:
2789 case Kind::kRelocatableWasmIndirectCallTarget:
2790 case Kind::kRelocatableWasmCanonicalSignatureId:
2791 return HashWithOptions(storage.integral);
2792 case Kind::kFloat32:
2793 return HashWithOptions(storage.float32.get_bits());
2794 case Kind::kFloat64:
2795 case Kind::kNumber:
2796 return HashWithOptions(storage.float64.get_bits());
2797 case Kind::kExternal:
2798 return HashWithOptions(strategy == HashingStrategy::kMakeSnapshotStable
2799 ? 0
2800 : storage.external.raw());
2801 case Kind::kHeapObject:
2802 case Kind::kCompressedHeapObject:
2803 case Kind::kTrustedHeapObject:
2804 if (strategy == HashingStrategy::kMakeSnapshotStable) {
2805 return HashWithOptions();
2806 }
2807 return HashWithOptions(storage.handle.address());
2808 }
2809 }
2810 bool operator==(const ConstantOp& other) const {
2811 if (kind != other.kind) return false;
2812 switch (kind) {
2813 case Kind::kWord32:
2814 case Kind::kWord64:
2815 case Kind::kSmi:
2816 case Kind::kTaggedIndex:
2817 case Kind::kRelocatableWasmCall:
2818 case Kind::kRelocatableWasmStubCall:
2819 case Kind::kRelocatableWasmCanonicalSignatureId:
2820 case Kind::kRelocatableWasmIndirectCallTarget:
2821 return storage.integral == other.storage.integral;
2822 case Kind::kFloat32:
2823 // Using a bit_cast to uint32_t in order to return false when comparing
2824 // +0 and -0.
2825 // Note: for JavaScript, it would be fine to return true when both
2826 // values are NaNs, but for Wasm we must not merge NaNs that way.
2827 // Since we canonicalize NaNs for JS anyway, we don't need to treat
2828 // them specially here.
2829 return base::bit_cast<uint32_t>(storage.float32) ==
2830 base::bit_cast<uint32_t>(other.storage.float32);
2831 case Kind::kFloat64:
2832 case Kind::kNumber:
2833 // Using a bit_cast to uint64_t in order to return false when comparing
2834 // +0 and -0.
2835 // Note: for JavaScript, it would be fine to return true when both
2836 // values are NaNs, but for Wasm we must not merge NaNs that way.
2837 // Since we canonicalize NaNs for JS anyway, we don't need to treat
2838 // them specially here.
2839 return base::bit_cast<uint64_t>(storage.float64) ==
2840 base::bit_cast<uint64_t>(other.storage.float64);
2841 case Kind::kExternal:
2842 return storage.external.raw() == other.storage.external.raw();
2843 case Kind::kHeapObject:
2844 case Kind::kCompressedHeapObject:
2845 case Kind::kTrustedHeapObject:
2846 return storage.handle.address() == other.storage.handle.address();
2847 }
2848 }
2849};
2850
2851// Load `loaded_rep` from: base + offset + index * 2^element_size_log2.
2852// For Kind::tagged_base: subtract kHeapObjectTag,
2853// `base` has to be the object start.
2854// For (u)int8/16, the value will be sign- or zero-extended to Word32.
2855// When result_rep is RegisterRepresentation::Compressed(), then the load does
2856// not decompress the value.
2857struct LoadOp : OperationT<LoadOp> {
2858 struct Kind {
2859 // The `base` input is a tagged pointer to a HeapObject.
2860 bool tagged_base : 1;
2861 // The effective address might be unaligned. This is only set to true if
2862 // the platform does not support unaligned loads for the given
2863 // MemoryRepresentation natively.
2865 // There is a Wasm trap handler for out-of-bounds accesses.
2867 // The wasm trap handler is used for null accesses. Note that this requires
2868 // with_trap_handler as well.
2870 // If {load_eliminable} is true, then:
2871 // - Stores/Loads at this address cannot overlap. Concretely, it means
2872 // that something like this cannot happen:
2873 //
2874 // const u32s = Uint32Array.of(3, 8);
2875 // const u8s = new Uint8Array(u32s.buffer);
2876 // u32s[0] = 0xffffffff;
2877 // u8s[1] = 0; // Overlaps with previous store!
2878 //
2879 // - Stores/Loads at this address have a canonical base. Concretely, it
2880 // means that something like this cannot happen:
2881 //
2882 // let buffer = new ArrayBuffer(10000);
2883 // let ta1 = new Int32Array(buffer, 0/*offset*/);
2884 // let ta2 = new Int32Array(buffer, 100*4/*offset*/);
2885 // ta2[0] = 0xff;
2886 // ta1[100] = 42; // Same destination as the previous store!
2887 //
2888 // - No other thread can modify the underlying value. E.g. in the case of
2889 // loading the wasm stack limit, other threads can modify the loaded
2890 // value, so we always have to reload it.
2891 //
2892 // This is mainly used for load elimination: when stores/loads don't have
2893 // the {load_eliminable} bit set to true, more things need to be
2894 // invalidated.
2895 // In the main JS pipeline, only ArrayBuffers (= TypedArray/DataView)
2896 // loads/stores have this {load_eliminable} set to false,
2897 // and all other loads have it to true.
2899 // The loaded value may not change.
2901 // The load should be atomic.
2902 bool is_atomic : 1;
2903
2904 static constexpr Kind Aligned(BaseTaggedness base_is_tagged) {
2905 switch (base_is_tagged) {
2906 case BaseTaggedness::kTaggedBase:
2907 return TaggedBase();
2908 case BaseTaggedness::kUntaggedBase:
2909 return RawAligned();
2910 }
2911 }
2912 static constexpr Kind TaggedBase() {
2913 return {.tagged_base = true,
2914 .maybe_unaligned = false,
2915 .with_trap_handler = false,
2916 .trap_on_null = false,
2917 .load_eliminable = true,
2918 .is_immutable = false,
2919 .is_atomic = false};
2920 }
2921 static constexpr Kind RawAligned() {
2922 return {.tagged_base = false,
2923 .maybe_unaligned = false,
2924 .with_trap_handler = false,
2925 .trap_on_null = false,
2926 .load_eliminable = true,
2927 .is_immutable = false,
2928 .is_atomic = false};
2929 }
2930 static constexpr Kind RawUnaligned() {
2931 return {.tagged_base = false,
2932 .maybe_unaligned = true,
2933 .with_trap_handler = false,
2934 .trap_on_null = false,
2935 .load_eliminable = true,
2936 .is_immutable = false,
2937 .is_atomic = false};
2938 }
2939 static constexpr Kind Protected() {
2940 return {.tagged_base = false,
2941 .maybe_unaligned = false,
2942 .with_trap_handler = true,
2943 .trap_on_null = false,
2944 .load_eliminable = true,
2945 .is_immutable = false,
2946 .is_atomic = false};
2947 }
2948 static constexpr Kind TrapOnNull() {
2949 return {.tagged_base = true,
2950 .maybe_unaligned = false,
2951 .with_trap_handler = true,
2952 .trap_on_null = true,
2953 .load_eliminable = true,
2954 .is_immutable = false,
2955 .is_atomic = false};
2956 }
2958 return rep == MemoryRepresentation::Int8() ||
2959 rep == MemoryRepresentation::Uint8() ||
2960 SupportedOperations::IsUnalignedLoadSupported(rep)
2961 ? LoadOp::Kind::RawAligned()
2962 : LoadOp::Kind::RawUnaligned();
2963 }
2964
2966 Kind new_kind = *this;
2967 new_kind.load_eliminable = false;
2968 return new_kind;
2969 }
2970
2971 constexpr Kind Immutable() const {
2972 Kind new_kind(*this);
2973 new_kind.is_immutable = true;
2974 return new_kind;
2975 }
2976
2977 constexpr Kind Atomic() const {
2978 Kind new_kind(*this);
2979 new_kind.is_atomic = true;
2980 return new_kind;
2981 }
2982
2983 bool operator==(const Kind& other) const {
2984 return tagged_base == other.tagged_base &&
2985 maybe_unaligned == other.maybe_unaligned &&
2986 with_trap_handler == other.with_trap_handler &&
2987 load_eliminable == other.load_eliminable &&
2988 is_immutable == other.is_immutable &&
2989 is_atomic == other.is_atomic && trap_on_null == other.trap_on_null;
2990 }
2991 };
2995 uint8_t element_size_log2; // multiply index with 2^element_size_log2
2996 int32_t offset; // add offset to scaled index
2997
2999 // Loads might depend on checks for pointer validity, object layout, bounds
3000 // checks, etc.
3001 // TODO(tebbi): Distinguish between on-heap and off-heap loads.
3003 if (kind.with_trap_handler) effects = effects.CanLeaveCurrentFunction();
3004 if (kind.is_atomic) {
3005 // Atomic load should not be reordered with other loads.
3006 effects = effects.CanWriteMemory();
3007 }
3008 return effects;
3009 }
3011 return base::VectorOf(&result_rep, 1);
3012 }
3013
3014 MachineType machine_type() const;
3015
3019 kind.tagged_base
3020 ? MaybeRepVector<MaybeRegisterRepresentation::Tagged(),
3021 MaybeRegisterRepresentation::WordPtr()>()
3022 : MaybeRepVector<MaybeRegisterRepresentation::WordPtr(),
3023 MaybeRegisterRepresentation::WordPtr()>();
3024 return index().valid() ? result : base::VectorOf(result.data(), 1);
3025 }
3026
3027 OpIndex base() const { return input(0); }
3029 return input_count == 2 ? input(1) : OpIndex::Invalid();
3030 }
3031
3032 static constexpr bool OffsetIsValid(int32_t offset, bool tagged_base) {
3033 if (tagged_base) {
3034 // When a Load has the tagged_base Kind, it means that {offset} will
3035 // eventually need a "-kHeapObjectTag". If the {offset} is
3036 // min_int, then subtracting kHeapObjectTag will underflow.
3037 return offset >= std::numeric_limits<int32_t>::min() + kHeapObjectTag;
3038 }
3039 return true;
3040 }
3041
3043 MemoryRepresentation loaded_rep, RegisterRepresentation result_rep,
3044 int32_t offset, uint8_t element_size_log2)
3045 : Base(1 + index.valid()),
3046 kind(kind),
3047 loaded_rep(loaded_rep),
3048 result_rep(result_rep),
3049 element_size_log2(element_size_log2),
3050 offset(offset) {
3051 input(0) = base;
3052 if (index.valid()) {
3053 input(1) = index.value();
3054 }
3055 }
3056
3057 template <typename Fn, typename Mapper>
3058 V8_INLINE auto Explode(Fn fn, Mapper& mapper) const {
3059 return fn(mapper.Map(base()), mapper.Map(index()), kind, loaded_rep,
3060 result_rep, offset, element_size_log2);
3061 }
3062
3063 void Validate(const Graph& graph) const {
3064 DCHECK(loaded_rep.ToRegisterRepresentation() == result_rep ||
3065 (loaded_rep.IsCompressibleTagged() &&
3066 result_rep == RegisterRepresentation::Compressed()) ||
3067 kind.is_atomic);
3068 DCHECK_IMPLIES(element_size_log2 > 0, index().valid());
3069 DCHECK_IMPLIES(kind.maybe_unaligned,
3070 !SupportedOperations::IsUnalignedLoadSupported(loaded_rep));
3071 DCHECK(OffsetIsValid(offset, kind.tagged_base));
3072 }
3073 static LoadOp& New(Graph* graph, OpIndex base, OptionalOpIndex index,
3074 Kind kind, MemoryRepresentation loaded_rep,
3075 RegisterRepresentation result_rep, int32_t offset,
3076 uint8_t element_size_log2) {
3077 return Base::New(graph, 1 + index.valid(), base, index, kind, loaded_rep,
3078 result_rep, offset, element_size_log2);
3079 }
3080 void PrintInputs(std::ostream& os, const std::string& op_index_prefix) const;
3081 void PrintOptions(std::ostream& os) const;
3082 auto options() const {
3083 return std::tuple{kind, loaded_rep, result_rep, offset, element_size_log2};
3084 }
3085};
3086
3087V8_INLINE size_t hash_value(LoadOp::Kind kind) {
3088 return base::hash_value(
3089 static_cast<int>(kind.tagged_base) | (kind.maybe_unaligned << 1) |
3090 (kind.load_eliminable << 2) | (kind.is_immutable << 3) |
3091 (kind.with_trap_handler << 4) | (kind.is_atomic << 5));
3092}
3093
3094struct AtomicRMWOp : OperationT<AtomicRMWOp> {
3095 enum class BinOp : uint8_t {
3096 kAdd,
3097 kSub,
3098 kAnd,
3099 kOr,
3100 kXor,
3101 kExchange,
3102 kCompareExchange
3103 };
3109 OpEffects effects =
3111 if (memory_access_kind == MemoryAccessKind::kProtectedByTrapHandler) {
3112 effects = effects.CanLeaveCurrentFunction();
3113 }
3114 return effects;
3115 }
3116
3118 return base::VectorOf(&in_out_rep, 1);
3119 }
3120
3123 if (bin_op == BinOp::kCompareExchange) {
3124 return InitVectorOf(
3125 storage, {RegisterRepresentation::WordPtr(),
3126 RegisterRepresentation::WordPtr(), in_out_rep, in_out_rep});
3127 }
3128 return InitVectorOf(storage,
3129 {RegisterRepresentation::WordPtr(),
3130 RegisterRepresentation::WordPtr(), in_out_rep});
3131 }
3132
3133 V<WordPtr> base() const { return input<WordPtr>(0); }
3134 V<WordPtr> index() const { return input<WordPtr>(1); }
3135 OpIndex value() const { return input(2); }
3137 return (input_count == 4) ? input(3) : OpIndex::Invalid();
3138 }
3139
3140 void Validate(const Graph& graph) const {
3141 DCHECK_EQ(bin_op == BinOp::kCompareExchange, expected().valid());
3142 }
3143
3145 OptionalOpIndex expected, BinOp bin_op,
3146 RegisterRepresentation in_out_rep,
3148 : Base(3 + expected.valid()),
3149 bin_op(bin_op),
3150 in_out_rep(in_out_rep),
3151 memory_rep(memory_rep),
3152 memory_access_kind(kind) {
3153 input(0) = base;
3154 input(1) = index;
3155 input(2) = value;
3156 if (expected.valid()) {
3157 input(3) = expected.value();
3158 }
3159 }
3160
3161 template <typename Fn, typename Mapper>
3162 V8_INLINE auto Explode(Fn fn, Mapper& mapper) const {
3163 return fn(mapper.Map(base()), mapper.Map(index()), mapper.Map(value()),
3164 mapper.Map(expected()), bin_op, in_out_rep, memory_rep,
3165 memory_access_kind);
3166 }
3167
3168 static AtomicRMWOp& New(Graph* graph, OpIndex base, OpIndex index,
3169 OpIndex value, OptionalOpIndex expected, BinOp bin_op,
3170 RegisterRepresentation result_rep,
3171 MemoryRepresentation input_rep,
3173 return Base::New(graph, 3 + expected.valid(), base, index, value, expected,
3174 bin_op, result_rep, input_rep, kind);
3175 }
3176
3177 void PrintInputs(std::ostream& os, const std::string& op_index_prefix) const;
3178
3179 void PrintOptions(std::ostream& os) const;
3180
3181 auto options() const {
3182 return std::tuple{bin_op, in_out_rep, memory_rep, memory_access_kind};
3183 }
3184};
3186
3187V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
3189
3191 enum class Kind : uint8_t {
3192 kAdd,
3193 kSub,
3194 kAnd,
3195 kOr,
3196 kXor,
3197 kExchange,
3198 kCompareExchange,
3199 kLoad,
3200 kStore
3201 };
3202
3204 int32_t offset;
3205
3207 switch (bin_op) {
3208 case AtomicRMWOp::BinOp::kAdd:
3209 return Kind::kAdd;
3210 case AtomicRMWOp::BinOp::kSub:
3211 return Kind::kSub;
3212 case AtomicRMWOp::BinOp::kAnd:
3213 return Kind::kAnd;
3214 case AtomicRMWOp::BinOp::kOr:
3215 return Kind::kOr;
3216 case AtomicRMWOp::BinOp::kXor:
3217 return Kind::kXor;
3218 case AtomicRMWOp::BinOp::kExchange:
3219 return Kind::kExchange;
3220 case AtomicRMWOp::BinOp::kCompareExchange:
3221 return Kind::kCompareExchange;
3222 }
3223 }
3224
3227 if (kind == Kind::kStore) {
3228 return effects.CanWriteMemory();
3229 }
3230 // Atomic loads are marked as "can write memory" as they should not be
3231 // reordered with other loads. Secondly, they may not be removed even if
3232 // unused as they might make writes of other threads visible.
3233 return effects.CanReadMemory().CanWriteMemory();
3234 }
3235
3237 if (kind == Kind::kStore) return {};
3238 return RepVector<RegisterRepresentation::Word32(),
3239 RegisterRepresentation::Word32()>();
3240 }
3243 storage.resize(input_count);
3244
3245 const bool has_index = HasIndex();
3246 storage[0] = RegisterRepresentation::WordPtr(); // base
3247 if (has_index) {
3248 storage[1] = RegisterRepresentation::WordPtr(); // index
3249 }
3250 if (kind != Kind::kLoad) {
3251 storage[1 + has_index] = RegisterRepresentation::Word32(); // value_low
3252 storage[2 + has_index] = RegisterRepresentation::Word32(); // value_high
3253 if (kind == Kind::kCompareExchange) {
3254 storage[3 + has_index] =
3255 RegisterRepresentation::Word32(); // expected_low
3256 storage[4 + has_index] =
3257 RegisterRepresentation::Word32(); // expected_high
3258 }
3259 }
3260 return base::VectorOf(storage);
3261 }
3262
3263 V<WordPtr> base() const { return input<WordPtr>(0); }
3265 return HasIndex() ? input<WordPtr>(1) : V<WordPtr>::Invalid();
3266 }
3268 return kind != Kind::kLoad ? input<Word32>(1 + HasIndex())
3270 }
3272 return kind != Kind::kLoad ? input<Word32>(2 + HasIndex())
3274 }
3276 return kind == Kind::kCompareExchange ? input<Word32>(3 + HasIndex())
3278 }
3280 return kind == Kind::kCompareExchange ? input<Word32>(4 + HasIndex())
3282 }
3283
3284
3286 OptionalV<Word32> value_low, OptionalV<Word32> value_high,
3287 OptionalV<Word32> expected_low,
3288 OptionalV<Word32> expected_high, Kind kind, int32_t offset)
3289 : Base(InputCount(kind, index.has_value())), kind(kind), offset(offset) {
3290 DCHECK_EQ(value_low.valid(), value_high.valid());
3291 DCHECK_EQ(expected_low.valid(), expected_high.valid());
3292 DCHECK_EQ(kind == Kind::kCompareExchange, expected_low.valid());
3293 DCHECK_EQ(kind != Kind::kLoad, value_low.valid());
3294
3295 const bool has_index = index.has_value();
3296 DCHECK_EQ(has_index, HasIndex());
3297
3298 input(0) = base;
3299 if (has_index) input(1) = index.value();
3300 if (kind != Kind::kLoad) {
3301 input(1 + has_index) = value_low.value();
3302 input(2 + has_index) = value_high.value();
3303 if (kind == Kind::kCompareExchange) {
3304 input(3 + has_index) = expected_low.value();
3305 input(4 + has_index) = expected_high.value();
3306 }
3307 }
3308 }
3309
3310 template <typename Fn, typename Mapper>
3311 V8_INLINE auto Explode(Fn fn, Mapper& mapper) const {
3312 return fn(mapper.Map(base()), mapper.Map(index()), mapper.Map(value_low()),
3313 mapper.Map(value_high()), mapper.Map(expected_low()),
3314 mapper.Map(expected_high()), kind, offset);
3315 }
3316
3317 static constexpr size_t InputCount(Kind kind, bool has_index) {
3318 switch (kind) {
3319 case Kind::kLoad:
3320 return 1 + has_index; // base, index?
3321 case Kind::kAdd:
3322 case Kind::kSub:
3323 case Kind::kAnd:
3324 case Kind::kOr:
3325 case Kind::kXor:
3326 case Kind::kExchange:
3327 case Kind::kStore:
3328 return 3 + has_index; // base, index?, value_low, value_high
3329 case Kind::kCompareExchange:
3330 return 5 + has_index; // base, index?, value_low, value_high,
3331 // expected_low, expected_high
3332 }
3333 }
3334 bool HasIndex() const { return input_count == InputCount(kind, true); }
3335
3337 OptionalV<WordPtr> index,
3338 OptionalV<Word32> value_low,
3339 OptionalV<Word32> value_high,
3340 OptionalV<Word32> expected_low,
3341 OptionalV<Word32> expected_high, Kind kind,
3342 int32_t offset) {
3343 return Base::New(graph, InputCount(kind, index.has_value()), base, index,
3344 value_low, value_high, expected_low, expected_high, kind,
3345 offset);
3346 }
3347
3348 void PrintInputs(std::ostream& os, const std::string& op_index_prefix) const;
3349
3350 void PrintOptions(std::ostream& os) const;
3351
3352 auto options() const { return std::tuple{kind, offset}; }
3353};
3354
3355V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
3357
3358struct MemoryBarrierOp : FixedArityOperationT<0, MemoryBarrierOp> {
3360
3361 static constexpr OpEffects effects =
3363
3364 explicit MemoryBarrierOp(AtomicMemoryOrder memory_order)
3365 : Base(), memory_order(memory_order) {}
3366
3368
3373
3374
3375 auto options() const { return std::tuple{memory_order}; }
3376 void PrintOptions(std::ostream& os) const;
3377};
3378
3379// Store `value` to: base + offset + index * 2^element_size_log2.
3380// For Kind::tagged_base: subtract kHeapObjectTag,
3381// `base` has to be the object start.
3382struct StoreOp : OperationT<StoreOp> {
3387 uint8_t element_size_log2; // multiply index with 2^element_size_log2
3388 int32_t offset; // add offset to scaled index
3390 uint16_t
3391 shifted_indirect_pointer_tag; // for indirect pointer stores, the
3392 // IndirectPointerTag of the store shifted
3393 // to the right by kIndirectPointerTagShift
3394 // (so it fits into 16 bits).
3395 // TODO(saelo): now that we have a pointer tag in these low-level operations,
3396 // we could also consider passing the external pointer tag (for external
3397 // pointers) through to the macro assembler (where we have routines to work
3398 // with external pointers) instead of handling those earlier in the compiler.
3399 // We might lose the ability to hardcode the table address though.
3400
3402 // Stores might depend on checks for pointer validity, object layout, bounds
3403 // checks, etc.
3404 // TODO(tebbi): Distinghish between on-heap and off-heap stores.
3406 if (kind.with_trap_handler) effects = effects.CanLeaveCurrentFunction();
3407 if (maybe_initializing_or_transitioning) {
3408 effects = effects.CanDoRawHeapAccess();
3409 }
3410 if (kind.is_atomic) {
3411 // Atomic stores should not be eliminated away, even if the situation
3412 // seems to allow e.g. store-store elimination. Elimination is avoided by
3413 // setting the `CanReadMemory` effect.
3414 effects = effects.CanReadMemory();
3415 }
3416 return effects;
3417 }
3419
3422 RegisterRepresentation base = kind.tagged_base
3423 ? RegisterRepresentation::Tagged()
3424 : RegisterRepresentation::WordPtr();
3425 if (index() == OpIndex::Invalid()) {
3426 return InitVectorOf(
3427 storage, {base, stored_rep.ToRegisterRepresentationForStore()});
3428 }
3429 return InitVectorOf(storage,
3431 RegisterRepresentation::WordPtr()});
3432 }
3433
3434 OpIndex base() const { return input(0); }
3435 OpIndex value() const { return input(1); }
3437 return input_count == 3 ? input(2) : OpIndex::Invalid();
3438 }
3439
3441 uint64_t shifted = shifted_indirect_pointer_tag;
3442 return static_cast<IndirectPointerTag>(shifted << kIndirectPointerTagShift);
3443 }
3444
3447 MemoryRepresentation stored_rep, WriteBarrierKind write_barrier,
3448 int32_t offset, uint8_t element_size_log2,
3449 bool maybe_initializing_or_transitioning,
3450 IndirectPointerTag maybe_indirect_pointer_tag = kIndirectPointerNullTag)
3451 : Base(2 + index.valid()),
3452 kind(kind),
3453 stored_rep(stored_rep),
3454 write_barrier(write_barrier),
3455 element_size_log2(element_size_log2),
3456 offset(offset),
3457 maybe_initializing_or_transitioning(
3458 maybe_initializing_or_transitioning),
3459 shifted_indirect_pointer_tag(maybe_indirect_pointer_tag >>
3461 DCHECK_EQ(indirect_pointer_tag(), maybe_indirect_pointer_tag);
3462 input(0) = base;
3463 input(1) = value;
3464 if (index.valid()) {
3465 input(2) = index.value();
3466 }
3467 }
3468
3469 template <typename Fn, typename Mapper>
3470 V8_INLINE auto Explode(Fn fn, Mapper& mapper) const {
3471 return fn(mapper.Map(base()), mapper.Map(index()), mapper.Map(value()),
3472 kind, stored_rep, write_barrier, offset, element_size_log2,
3473 maybe_initializing_or_transitioning, indirect_pointer_tag());
3474 }
3475
3476 void Validate(const Graph& graph) const {
3477 DCHECK_IMPLIES(element_size_log2 > 0, index().valid());
3478 DCHECK_IMPLIES(kind.maybe_unaligned,
3479 !SupportedOperations::IsUnalignedLoadSupported(stored_rep));
3480 DCHECK(LoadOp::OffsetIsValid(offset, kind.tagged_base));
3481 }
3482 static StoreOp& New(
3483 Graph* graph, OpIndex base, OptionalOpIndex index, OpIndex value,
3484 Kind kind, MemoryRepresentation stored_rep,
3485 WriteBarrierKind write_barrier, int32_t offset, uint8_t element_size_log2,
3486 bool maybe_initializing_or_transitioning,
3487 IndirectPointerTag maybe_indirect_pointer_tag = kIndirectPointerNullTag) {
3488 return Base::New(graph, 2 + index.valid(), base, index, value, kind,
3489 stored_rep, write_barrier, offset, element_size_log2,
3490 maybe_initializing_or_transitioning,
3491 maybe_indirect_pointer_tag);
3492 }
3493
3494 void PrintInputs(std::ostream& os, const std::string& op_index_prefix) const;
3495 void PrintOptions(std::ostream& os) const;
3496 auto options() const {
3497 return std::tuple{
3498 kind, stored_rep, write_barrier,
3499 offset, element_size_log2, maybe_initializing_or_transitioning};
3500 }
3501};
3502
3503struct AllocateOp : FixedArityOperationT<1, AllocateOp> {
3505
3506 static constexpr OpEffects effects =
3507 OpEffects()
3508 .CanAllocate()
3509 // The resulting object is unitialized, which leaves the heap in an
3510 // inconsistent state.
3512 // Do not move allocations before checks, to avoid OOM or invalid
3513 // size.
3516 return RepVector<RegisterRepresentation::Tagged()>();
3517 }
3518
3521 return MaybeRepVector<MaybeRegisterRepresentation::WordPtr()>();
3522 }
3523
3524 V<WordPtr> size() const { return input<WordPtr>(0); }
3525
3526 AllocateOp(V<WordPtr> size, AllocationType type) : Base(size), type(type) {}
3527
3528 void PrintOptions(std::ostream& os) const;
3529
3530 auto options() const { return std::tuple{type}; }
3531};
3532
3534 : FixedArityOperationT<1, DecodeExternalPointerOp> {
3536
3537 // Accessing external pointers is only safe if the garbage collected pointer
3538 // keeping the external pointer alive is retained for the length of the
3539 // operation. For this, it is essential that we use a `Retain` operation
3540 // placed after the last access to the external data.
3541 static constexpr OpEffects effects = OpEffects();
3543 return RepVector<RegisterRepresentation::WordPtr()>();
3544 }
3545
3548 return MaybeRepVector<MaybeRegisterRepresentation::Word32()>();
3549 }
3550
3551 OpIndex handle() const { return input(0); }
3552
3554 : Base(handle), tag_range(tag_range) {}
3555
3556 void Validate(const Graph& graph) const { DCHECK(!tag_range.IsEmpty()); }
3557 void PrintOptions(std::ostream& os) const;
3558 auto options() const { return std::tuple{tag_range}; }
3559};
3560
3561struct JSStackCheckOp : OperationT<JSStackCheckOp> {
3562 enum class Kind : uint8_t { kFunctionEntry, kBuiltinEntry, kLoop };
3564
3566 switch (kind) {
3567 case Kind::kFunctionEntry:
3568 return OpEffects().CanCallAnything();
3569 case Kind::kBuiltinEntry:
3570 return OpEffects().CanCallAnything();
3571 case Kind::kLoop:
3572 // Loop body iteration stack checks can't write memory.
3573 // TODO(dmercadier): we could prevent this from allocating. In
3574 // particular, we'd need to:
3575 // - forbid GC interrupts from being processed in loop stack checks.
3576 // - make sure that the debugger always deopts the current function
3577 // when it triggers a loop interrupt.
3578 return OpEffects()
3580 .CanDeopt()
3582 .CanAllocate();
3583 }
3584 }
3585
3586 V<Context> native_context() const { return Base::input<Context>(0); }
3588 return input_count > 1 ? Base::input<FrameState>(1)
3590 }
3591
3593
3598
3599 explicit JSStackCheckOp(V<Context> context, OptionalV<FrameState> frame_state,
3600 Kind kind)
3601 : Base(1 + frame_state.has_value()), kind(kind) {
3602 input(0) = context;
3603 if (frame_state.has_value()) {
3604 input(1) = frame_state.value();
3605 }
3606 }
3607
3608 static JSStackCheckOp& New(Graph* graph, V<Context> context,
3609 OptionalV<FrameState> frame_state, Kind kind) {
3610 return Base::New(graph, 1 + frame_state.has_value(), context, frame_state,
3611 kind);
3612 }
3613
3614 void Validate(const Graph& graph) const {
3615 DCHECK_EQ(kind == Kind::kBuiltinEntry, !frame_state().has_value());
3616 }
3617
3618 template <typename Fn, typename Mapper>
3619 V8_INLINE auto Explode(Fn fn, Mapper& mapper) const {
3620 return fn(mapper.Map(native_context()), mapper.Map(frame_state()), kind);
3621 }
3622
3623 auto options() const { return std::tuple{kind}; }
3624};
3625
3626// Retain a HeapObject to prevent it from being garbage collected too early.
3627struct RetainOp : FixedArityOperationT<1, RetainOp> {
3628 V<Object> retained() const { return input<Object>(0); }
3629
3630 // Retain doesn't actually write, it just keeps a value alive. However, since
3631 // this must not be reordered with reading operations, we mark it as writing.
3632 static constexpr OpEffects effects = OpEffects().CanWriteMemory();
3634
3637 return MaybeRepVector<MaybeRegisterRepresentation::Tagged()>();
3638 }
3639
3640 explicit RetainOp(V<Object> retained) : Base(retained) {}
3641
3642 auto options() const { return std::tuple{}; }
3643};
3644
3645// We compare the stack pointer register with the given limit and a
3646// codegen-dependant adjustment.
3648 : FixedArityOperationT<1, StackPointerGreaterThanOp> {
3650
3651 // Since the frame size of optimized functions is constant, this behaves like
3652 // a pure operation.
3653 static constexpr OpEffects effects = OpEffects();
3655 return RepVector<RegisterRepresentation::Word32()>();
3656 }
3657
3660 return MaybeRepVector<MaybeRegisterRepresentation::WordPtr()>();
3661 }
3662
3663 V<WordPtr> stack_limit() const { return input<WordPtr>(0); }
3664
3666 : Base(stack_limit), kind(kind) {}
3667
3668 auto options() const { return std::tuple{kind}; }
3669};
3670
3671// Allocate a piece of memory in the current stack frame. Every operation
3672// in the IR is a separate stack slot, but repeated execution in a loop
3673// produces the same stack slot.
3674struct StackSlotOp : FixedArityOperationT<0, StackSlotOp> {
3675 int size;
3678
3679 // We can freely reorder stack slot operations, but must not de-duplicate
3680 // them.
3681 static constexpr OpEffects effects = OpEffects().CanCreateIdentity();
3683 return RepVector<RegisterRepresentation::WordPtr()>();
3684 }
3685
3690
3691 StackSlotOp(int size, int alignment, bool is_tagged = false)
3692 : size(size), alignment(alignment), is_tagged(is_tagged) {}
3693 auto options() const { return std::tuple{size, alignment, is_tagged}; }
3694};
3695
3696// Values that are constant for the current stack frame/invocation.
3697// Therefore, they behaves like a constant, even though they are different for
3698// every invocation.
3699struct FrameConstantOp : FixedArityOperationT<0, FrameConstantOp> {
3700 enum class Kind : uint8_t {
3701 kStackCheckOffset,
3702 kFramePointer,
3703 kParentFramePointer
3704 };
3706
3707 static constexpr OpEffects effects = OpEffects();
3709 switch (kind) {
3710 case Kind::kStackCheckOffset:
3711 return RepVector<RegisterRepresentation::Tagged()>();
3712 case Kind::kFramePointer:
3713 case Kind::kParentFramePointer:
3714 return RepVector<RegisterRepresentation::WordPtr()>();
3715 }
3716 }
3717
3722
3724 auto options() const { return std::tuple{kind}; }
3725};
3726V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
3728
3729struct FrameStateOp : OperationT<FrameStateOp> {
3732
3733 static constexpr OpEffects effects = OpEffects();
3735
3740
3742 DCHECK(inlined);
3743 return input(0);
3744 }
3747 if (inlined) result += 1;
3748 return result;
3749 }
3750 uint16_t state_values_count() const {
3751 DCHECK_EQ(input_count - inlined, state_values().size());
3752 return input_count - inlined;
3753 }
3754 const OpIndex state_value(size_t idx) const { return state_values()[idx]; }
3755
3757 return RegisterRepresentation::FromMachineRepresentation(
3758 data->machine_types[idx].representation());
3759 }
3760
3762 const FrameStateData* data)
3763 : Base(inputs), inlined(inlined), data(data) {}
3764
3765 template <typename Fn, typename Mapper>
3766 V8_INLINE auto Explode(Fn fn, Mapper& mapper) const {
3767 auto mapped_inputs = mapper.template Map<32>(inputs());
3768 return fn(base::VectorOf(mapped_inputs), inlined, data);
3769 }
3770
3772 HashingStrategy strategy = HashingStrategy::kDefault) const {
3773 // Computing the full hash of {data} would be a bit slow, so we just use the
3774 // `bailout_id` and `inlined` (+ the inputs) to get a quick hash (and rely
3775 // on the == operator to properly check later if 2 FrameStateOp with the
3776 // same hash are really equal)
3777 return HashWithOptions(inlined, data->frame_state_info.bailout_id());
3778 }
3779
3780 V8_EXPORT_PRIVATE void Validate(const Graph& graph) const;
3781 V8_EXPORT_PRIVATE void PrintOptions(std::ostream& os) const;
3782 auto options() const { return std::tuple{inlined, *data}; }
3783};
3784
3785struct DeoptimizeOp : FixedArityOperationT<1, DeoptimizeOp> {
3787
3788 static constexpr OpEffects effects = OpEffects().CanDeopt();
3790
3795
3796 V<FrameState> frame_state() const { return input<FrameState>(0); }
3797
3799 const DeoptimizeParameters* parameters)
3800 : Base(frame_state), parameters(parameters) {}
3801 void Validate(const Graph& graph) const {
3802 DCHECK(Get(graph, frame_state()).Is<FrameStateOp>());
3803 }
3804 auto options() const { return std::tuple{parameters}; }
3805};
3806
3807struct DeoptimizeIfOp : FixedArityOperationT<2, DeoptimizeIfOp> {
3810
3811 static constexpr OpEffects effects = OpEffects().CanDeopt();
3813
3816 return MaybeRepVector<MaybeRegisterRepresentation::Word32()>();
3817 }
3818
3819 V<Word32> condition() const { return input<Word32>(0); }
3820 V<FrameState> frame_state() const { return input<FrameState>(1); }
3821
3823 const DeoptimizeParameters* parameters)
3824 : Base(condition, frame_state),
3825 negated(negated),
3826 parameters(parameters) {}
3827
3828 bool EqualsForGVN(const DeoptimizeIfOp& other) const {
3829 // As far as GVN is concerned, the `frame_state` and `parameters` don't
3830 // matter: 2 DeoptimizeIf can be GVNed if they have the same `condition` and
3831 // same `negated`, regardless of their `frame_state` and `parameters`.
3832 return condition() == other.condition() && negated == other.negated;
3833 }
3835 HashingStrategy strategy = HashingStrategy::kDefault) const {
3836 // To enable GVNing as described above in `EqualsForGVN`, `hash_value` has
3837 // to ignore the `frame_state` and the `parameters`.
3838 return fast_hash_combine(Opcode::kDeoptimizeIf, condition(), negated);
3839 }
3840 void Validate(const Graph& graph) const {
3841 DCHECK(Get(graph, frame_state()).Is<FrameStateOp>());
3842 }
3843 auto options() const { return std::tuple{negated, parameters}; }
3844 void PrintOptions(std::ostream& os) const;
3845};
3846
3847#if V8_ENABLE_WEBASSEMBLY
3848
3849struct WasmStackCheckOp : FixedArityOperationT<0, WasmStackCheckOp> {
3850 using Kind = JSStackCheckOp::Kind;
3851 Kind kind;
3852
3853 static constexpr OpEffects effects = OpEffects().CanCallAnything();
3854
3855 explicit WasmStackCheckOp(Kind kind) : Base(), kind(kind) {}
3856
3857 base::Vector<const RegisterRepresentation> outputs_rep() const { return {}; }
3858
3859 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
3860 ZoneVector<MaybeRegisterRepresentation>& storage) const {
3861 return {};
3862 }
3863
3864
3865 auto options() const { return std::tuple{kind}; }
3866};
3867
3868V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
3869 WasmStackCheckOp::Kind kind);
3870struct TrapIfOp : OperationT<TrapIfOp> {
3871 bool negated;
3872 const TrapId trap_id;
3873
3874 static constexpr OpEffects effects =
3875 OpEffects()
3876 // Traps must not float above a protective check.
3878 // Subsequent code can rely on the trap not having happened.
3880 base::Vector<const RegisterRepresentation> outputs_rep() const { return {}; }
3881
3882 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
3883 ZoneVector<MaybeRegisterRepresentation>& storage) const {
3884 return MaybeRepVector<MaybeRegisterRepresentation::Word32()>();
3885 }
3886
3887 V<Word32> condition() const { return input<Word32>(0); }
3888 OptionalV<FrameState> frame_state() const {
3889 return input_count > 1 ? input<FrameState>(1)
3890 : OptionalV<FrameState>::Nullopt();
3891 }
3892
3893 TrapIfOp(V<Word32> condition, OptionalV<FrameState> frame_state, bool negated,
3894 const TrapId trap_id)
3895 : Base(1 + frame_state.valid()), negated(negated), trap_id(trap_id) {
3896 input(0) = condition;
3897 if (frame_state.valid()) {
3898 input(1) = frame_state.value();
3899 }
3900 }
3901
3902 template <typename Fn, typename Mapper>
3903 V8_INLINE auto Explode(Fn fn, Mapper& mapper) const {
3904 return fn(mapper.Map(condition()), mapper.Map(frame_state()), negated,
3905 trap_id);
3906 }
3907
3908 static TrapIfOp& New(Graph* graph, V<Word32> condition,
3909 OptionalV<FrameState> frame_state, bool negated,
3910 const TrapId trap_id) {
3911 return Base::New(graph, 1 + frame_state.valid(), condition, frame_state,
3912 negated, trap_id);
3913 }
3914
3915 void Validate(const Graph& graph) const {
3916 if (frame_state().valid()) {
3917 DCHECK(Get(graph, frame_state().value()).Is<FrameStateOp>());
3918 }
3919 }
3920 auto options() const { return std::tuple{negated, trap_id}; }
3921};
3922#endif // V8_ENABLE_WEBASSEMBLY
3923
3924struct StaticAssertOp : FixedArityOperationT<1, StaticAssertOp> {
3925 const char* source;
3926 static constexpr OpEffects effects =
3928
3930
3933 return MaybeRepVector<MaybeRegisterRepresentation::Word32()>();
3934 }
3935
3936 V<Word32> condition() const { return Base::input<Word32>(0); }
3937
3939 : Base(condition), source(source) {}
3940
3941 auto options() const { return std::tuple{source}; }
3942};
3943
3944struct ParameterOp : FixedArityOperationT<0, ParameterOp> {
3947 const char* debug_name;
3948
3949 static constexpr OpEffects effects = OpEffects();
3951 return {&rep, 1};
3952 }
3953
3956 return {}; // On the callee side a parameter doesn't have an input.
3957 }
3958
3959 explicit ParameterOp(int32_t parameter_index, RegisterRepresentation rep,
3960 const char* debug_name = "")
3961 : Base(),
3962 parameter_index(parameter_index),
3963 rep(rep),
3964 debug_name(debug_name) {}
3965 auto options() const { return std::tuple{parameter_index, rep, debug_name}; }
3966 void PrintOptions(std::ostream& os) const;
3967};
3968
3969struct OsrValueOp : FixedArityOperationT<0, OsrValueOp> {
3970 int32_t index;
3971
3972 static constexpr OpEffects effects = OpEffects();
3974 return RepVector<RegisterRepresentation::Tagged()>();
3975 }
3976
3981
3982 explicit OsrValueOp(int32_t index) : Base(), index(index) {}
3983 auto options() const { return std::tuple{index}; }
3984};
3985
3986struct TSCallDescriptor : public NON_EXPORTED_BASE(ZoneObject) {
3992 // TODO(dlehmann,353475584): Since the `JSWasmCallParameters` are specific to
3993 // one particular call site, this assumes that (only works correctly if)
3994 // `TSCallDescriptor`s are not shared across different calls (which they are
3995 // not at the moment).
3996 // For sharing call descriptors, the `JSWasmCallParameters` need to be moved
3997 // to the CallOp, which causes a lot of code churn (needs touching all
3998 // `REDUCE(Call)`).
3999 const JSWasmCallParameters* js_wasm_call_parameters;
4000
4004 CanThrow can_throw, LazyDeoptOnThrow lazy_deopt_on_throw,
4005 const JSWasmCallParameters* js_wasm_call_parameters)
4006 : descriptor(descriptor),
4007 in_reps(in_reps),
4008 out_reps(out_reps),
4009 can_throw(can_throw),
4010 lazy_deopt_on_throw(lazy_deopt_on_throw),
4011 js_wasm_call_parameters(js_wasm_call_parameters) {}
4012
4014 const CallDescriptor* descriptor, CanThrow can_throw,
4015 LazyDeoptOnThrow lazy_deopt_on_throw, Zone* graph_zone,
4016 const JSWasmCallParameters* js_wasm_call_parameters = nullptr) {
4017 DCHECK_IMPLIES(can_throw == CanThrow::kNo,
4018 lazy_deopt_on_throw == LazyDeoptOnThrow::kNo);
4020 graph_zone->AllocateVector<RegisterRepresentation>(
4021 descriptor->ParameterCount());
4022 for (size_t i = 0; i < descriptor->ParameterCount(); ++i) {
4023 in_reps[i] = RegisterRepresentation::FromMachineRepresentation(
4024 descriptor->GetParameterType(i).representation());
4025 }
4027 graph_zone->AllocateVector<RegisterRepresentation>(
4028 descriptor->ReturnCount());
4029 for (size_t i = 0; i < descriptor->ReturnCount(); ++i) {
4030 out_reps[i] = RegisterRepresentation::FromMachineRepresentation(
4031 descriptor->GetReturnType(i).representation());
4032 }
4033 return graph_zone->New<TSCallDescriptor>(descriptor, in_reps, out_reps,
4034 can_throw, lazy_deopt_on_throw,
4035 js_wasm_call_parameters);
4036 }
4037};
4038
4039template <>
4041 size_t operator()(const TSCallDescriptor& v) {
4042 const CallDescriptor& d = *v.descriptor;
4043 // This does not include all fields of the call descriptor, but it should be
4044 // sufficient to differentiate between different calls (and collisions are
4045 // not too critical).
4046 return fast_hash_combine(d.kind(), d.tag(), d.ReturnCount(),
4047 d.ParameterCount(), d.GPParameterCount(),
4048 d.FPParameterCount(), d.ParameterSlotCount(),
4049 d.ReturnSlotCount(), d.flags());
4050 }
4051};
4052
4053// If {target} is a HeapObject representing a builtin, return that builtin's ID.
4054std::optional<Builtin> TryGetBuiltinId(const ConstantOp* target,
4056
4057struct CallOp : OperationT<CallOp> {
4060
4061 OpEffects Effects() const { return callee_effects; }
4062
4063 // The outputs are produced by the `DidntThrow` operation.
4066 return descriptor->out_reps;
4067 }
4068
4071 storage.resize(input_count);
4072 size_t i = 0;
4073 if (descriptor->descriptor->IsCodeObjectCall() ||
4074 descriptor->descriptor->IsJSFunctionCall() ||
4075 descriptor->descriptor->IsBuiltinPointerCall()) {
4076 storage[i++] = MaybeRegisterRepresentation::Tagged();
4077#ifdef V8_ENABLE_WEBASSEMBLY
4078 } else if (descriptor->descriptor->IsIndirectWasmFunctionCall()) {
4079 storage[i++] = MaybeRegisterRepresentation::Word32();
4080#endif
4081 } else {
4082 storage[i++] = MaybeRegisterRepresentation::WordPtr();
4083 }
4084 if (HasFrameState()) {
4085 storage[i++] = MaybeRegisterRepresentation::None();
4086 }
4087 for (auto rep : descriptor->in_reps) {
4088 // In JavaScript, parameters are optional.
4089 if (i >= input_count) break;
4090 storage[i++] = rep;
4091 }
4092 storage.resize(i);
4093 return base::VectorOf(storage);
4094 }
4095
4096 bool HasFrameState() const {
4097 return descriptor->descriptor->NeedsFrameState();
4098 }
4099
4100 V<CallTarget> callee() const { return input<CallTarget>(0); }
4102 return HasFrameState() ? input<FrameState>(1)
4104 }
4106 return inputs().SubVector(1 + HasFrameState(), input_count);
4107 }
4108 // Returns true if this call is a JS (but not wasm) stack check.
4109 V8_EXPORT_PRIVATE bool IsStackCheck(const Graph& graph, JSHeapBroker* broker,
4110 StackCheckKind kind) const;
4111
4114 const TSCallDescriptor* descriptor, OpEffects effects)
4115 : Base(1 + frame_state.valid() + arguments.size()),
4116 descriptor(descriptor),
4117 callee_effects(effects) {
4118 base::Vector<OpIndex> inputs = this->inputs();
4119 inputs[0] = callee;
4120 if (frame_state.valid()) {
4121 inputs[1] = frame_state.value();
4122 }
4123 inputs.SubVector(1 + frame_state.valid(), inputs.size())
4124 .OverwriteWith(arguments);
4125 }
4126
4127 template <typename Fn, typename Mapper>
4128 V8_INLINE auto Explode(Fn fn, Mapper& mapper) const {
4129 V<CallTarget> mapped_callee = mapper.Map(callee());
4130 OptionalV<FrameState> mapped_frame_state = mapper.Map(frame_state());
4131 auto mapped_arguments = mapper.template Map<16>(arguments());
4132 return fn(mapped_callee, mapped_frame_state,
4133 base::VectorOf(mapped_arguments), descriptor, Effects());
4134 }
4135
4136 void Validate(const Graph& graph) const {
4137 if (frame_state().valid()) {
4138 DCHECK(Get(graph, frame_state().value()).Is<FrameStateOp>());
4139 }
4140 }
4141
4142 static CallOp& New(Graph* graph, V<CallTarget> callee,
4143 OptionalV<FrameState> frame_state,
4145 const TSCallDescriptor* descriptor, OpEffects effects) {
4146 return Base::New(graph, 1 + frame_state.valid() + arguments.size(), callee,
4147 frame_state, arguments, descriptor, effects);
4148 }
4149 // TODO(mliedtke): Should the hash function be overwritten, so that calls (and
4150 // potentially tail calls) can participate in GVN? Right now this is prevented
4151 // by every call descriptor being a different pointer.
4152 auto options() const { return std::tuple{descriptor, callee_effects}; }
4153 size_t hash_value(HashingStrategy strategy = HashingStrategy::kDefault) const;
4154 void PrintOptions(std::ostream& os) const;
4155};
4156
4157// Catch an exception from the first operation of the `successor` block and
4158// continue execution in `catch_block` in this case.
4159struct CheckExceptionOp : FixedArityOperationT<1, CheckExceptionOp> {
4162
4163 static constexpr OpEffects effects = OpEffects().CanCallAnything();
4165
4166 V<Any> throwing_operation() const { return input<Any>(0); }
4167
4172
4173 CheckExceptionOp(V<Any> throwing_operation, Block* successor,
4174 Block* catch_block)
4175 : Base(throwing_operation),
4176 didnt_throw_block(successor),
4177 catch_block(catch_block) {}
4178
4179 V8_EXPORT_PRIVATE void Validate(const Graph& graph) const;
4180
4181 size_t hash_value(HashingStrategy strategy = HashingStrategy::kDefault) const;
4182 auto options() const { return std::tuple{didnt_throw_block, catch_block}; }
4183};
4184
4185// This is a pseudo-operation that marks the beginning of a catch block. It
4186// returns the caught exception.
4187struct CatchBlockBeginOp : FixedArityOperationT<0, CatchBlockBeginOp> {
4188 static constexpr OpEffects effects = OpEffects().CanCallAnything();
4189
4191 return RepVector<RegisterRepresentation::Tagged()>();
4192 }
4193
4198
4200 auto options() const { return std::tuple{}; }
4201};
4202
4203// Throwing operations always appear together with `DidntThrowOp`, which
4204// produces the value in case that no exception was thrown. If the callsite is
4205// non-catching, then `DidntThrowOp` follows right after the throwing operation:
4206//
4207// 100: Call(...)
4208// 101: DidntThrow(100)
4209// 102: Foo(101)
4210//
4211// If the callsite can catch, then the
4212// pattern is as follows:
4213//
4214// 100: Call(...)
4215// 101: CheckException(B10, B11)
4216//
4217// B10:
4218// 102: DidntThrow(100)
4219// 103: Foo(102)
4220//
4221// B11:
4222// 200: CatchBlockBegin()
4223// 201: ...
4224//
4225// This complexity is mostly hidden from graph creation, with
4226// `DidntThrowOp` and `CheckExceptionOp` being inserted automatically.
4227// The correct way to produce `CheckExceptionOp` is to create an
4228// `Assembler::CatchScope`, which will cause all throwing operations
4229// to add a `CheckExceptionOp` automatically while the scope is active.
4230// Since `CopyingPhase` does this automatically, lowering throwing
4231// operations into an arbitrary subgraph works automatically.
4232struct DidntThrowOp : FixedArityOperationT<1, DidntThrowOp> {
4234
4235 // If there is a `CheckException` operation with a catch block for
4236 // `throwing_operation`.
4238 // This is a pointer to a vector instead of a vector to save a bit of memory,
4239 // using optimal 16 bytes instead of 24.
4241
4242 OpEffects Effects() const { return throwing_op_effects; }
4243
4245 return *results_rep;
4246 }
4247
4250 return MaybeRepVector<MaybeRegisterRepresentation::None()>();
4251 }
4252
4253 OpIndex throwing_operation() const { return input(0); }
4254
4256 OpIndex throwing_operation, bool has_catch_block,
4258 OpEffects throwing_op_effects)
4259 : Base(throwing_operation),
4260 throwing_op_effects(throwing_op_effects),
4261 has_catch_block(has_catch_block),
4262 results_rep(results_rep) {}
4263 V8_EXPORT_PRIVATE void Validate(const Graph& graph) const;
4264 auto options() const {
4265 return std::tuple{throwing_op_effects, has_catch_block};
4266 }
4267};
4268
4269struct TailCallOp : OperationT<TailCallOp> {
4271
4272 static constexpr OpEffects effects = OpEffects().CanLeaveCurrentFunction();
4274 // While TailCalls do return some values, those values are returned to the
4275 // caller rather than to the current function (and a TailCallOp thus never
4276 // has any uses), so we set the outputs_rep to empty. If you need to know
4277 // what a TailCallOp returns, you can find out in `descriptor->outputs_rep`.
4278 return {};
4279 }
4280
4283 storage.resize(input_count);
4284 size_t i = 0;
4285#ifdef V8_ENABLE_WEBASSEMBLY
4286 if (descriptor->descriptor->IsIndirectWasmFunctionCall()) {
4287 storage[i++] = MaybeRegisterRepresentation::Word32();
4288 } else if (descriptor->descriptor->IsDirectWasmFunctionCall() ||
4289 descriptor->descriptor->IsWasmImportWrapper() ||
4290 descriptor->descriptor->IsWasmCapiFunction()) {
4291 storage[i++] = MaybeRegisterRepresentation::WordPtr();
4292 } else {
4293 storage[i++] = MaybeRegisterRepresentation::Tagged();
4294 }
4295#else
4296 storage[i++] = MaybeRegisterRepresentation::Tagged();
4297#endif
4298 for (auto rep : descriptor->in_reps) {
4299 storage[i++] = rep;
4300 }
4301 storage.resize(i);
4302 return base::VectorOf(storage);
4303 }
4304
4305 OpIndex callee() const { return input(0); }
4307 return inputs().SubVector(1, input_count);
4308 }
4309
4311 const TSCallDescriptor* descriptor)
4312 : Base(1 + arguments.size()), descriptor(descriptor) {
4313 base::Vector<OpIndex> inputs = this->inputs();
4314 inputs[0] = callee;
4315 inputs.SubVector(1, inputs.size()).OverwriteWith(arguments);
4316 }
4317
4318 template <typename Fn, typename Mapper>
4319 V8_INLINE auto Explode(Fn fn, Mapper& mapper) const {
4320 OpIndex mapped_callee = mapper.Map(callee());
4321 auto mapped_arguments = mapper.template Map<16>(arguments());
4322 return fn(mapped_callee, base::VectorOf(mapped_arguments), descriptor);
4323 }
4324
4325 static TailCallOp& New(Graph* graph, OpIndex callee,
4327 const TSCallDescriptor* descriptor) {
4328 return Base::New(graph, 1 + arguments.size(), callee, arguments,
4329 descriptor);
4330 }
4331 auto options() const { return std::tuple{descriptor}; }
4332 void PrintOptions(std::ostream& os) const;
4333};
4334
4335// Control-flow should never reach here.
4336struct UnreachableOp : FixedArityOperationT<0, UnreachableOp> {
4337 static constexpr OpEffects effects =
4340
4345
4347 auto options() const { return std::tuple{}; }
4348};
4349
4350struct ReturnOp : OperationT<ReturnOp> {
4351 // spill_caller_frame_slots signals that all caller stack located return
4352 // values should be spilled before reaching the InstructionSelector.
4353 // The growable stacks implementation does extra work to spill these values
4354 // and it cannot be performed during InstructionSelector lowering efficiently.
4356 static constexpr OpEffects effects = OpEffects().CanLeaveCurrentFunction();
4358
4361 // TODO(mliedtke): Ideally, a return op would expect to get the correct
4362 // types for all its return values, not just the pop count.
4363 return MaybeRepVector<MaybeRegisterRepresentation::Word32()>();
4364 }
4365
4366 // Number of additional stack slots to be removed.
4367 V<Word32> pop_count() const { return input<Word32>(0); }
4368
4370 return inputs().SubVector(1, input_count);
4371 }
4372
4374 bool spill_caller_frame_slots)
4375 : Base(1 + return_values.size()),
4376 spill_caller_frame_slots(spill_caller_frame_slots) {
4377 base::Vector<OpIndex> inputs = this->inputs();
4378 inputs[0] = pop_count;
4379 inputs.SubVector(1, inputs.size()).OverwriteWith(return_values);
4380 }
4381
4382 template <typename Fn, typename Mapper>
4383 V8_INLINE auto Explode(Fn fn, Mapper& mapper) const {
4384 OpIndex mapped_pop_count = mapper.Map(pop_count());
4385 auto mapped_return_values = mapper.template Map<4>(return_values());
4386 return fn(mapped_pop_count, base::VectorOf(mapped_return_values),
4387 spill_caller_frame_slots);
4388 }
4389
4390 static ReturnOp& New(Graph* graph, V<Word32> pop_count,
4391 base::Vector<const OpIndex> return_values,
4392 bool spill_caller_frame_slots) {
4393 return Base::New(graph, 1 + return_values.size(), pop_count, return_values,
4394 spill_caller_frame_slots);
4395 }
4396 auto options() const { return std::tuple{spill_caller_frame_slots}; }
4397};
4398
4399struct GotoOp : FixedArityOperationT<0, GotoOp> {
4402
4403 static constexpr OpEffects effects = OpEffects().CanChangeControlFlow();
4405
4410
4411 explicit GotoOp(Block* destination, bool is_backedge)
4412 : Base(), is_backedge(is_backedge), destination(destination) {}
4413 size_t hash_value(HashingStrategy strategy = HashingStrategy::kDefault) const;
4414 auto options() const { return std::tuple{destination, is_backedge}; }
4415};
4416
4417struct BranchOp : FixedArityOperationT<1, BranchOp> {
4421
4422 static constexpr OpEffects effects = OpEffects().CanChangeControlFlow();
4424
4427 return MaybeRepVector<MaybeRegisterRepresentation::Word32()>();
4428 }
4429
4430 V<Word32> condition() const { return input<Word32>(0); }
4431
4433 BranchHint hint)
4434 : Base(condition), hint(hint), if_true(if_true), if_false(if_false) {}
4435
4436 size_t hash_value(HashingStrategy strategy = HashingStrategy::kDefault) const;
4437 auto options() const { return std::tuple{if_true, if_false, hint}; }
4438};
4439
4440struct SwitchOp : FixedArityOperationT<1, SwitchOp> {
4441 struct Case {
4443 int32_t value;
4445
4446 Case(int32_t value, Block* destination, BranchHint hint)
4447 : hint(hint), value(value), destination(destination) {}
4448
4449 bool operator==(const Case& other) const {
4450 return value == other.value && destination == other.destination &&
4451 hint == other.hint;
4452 }
4453 };
4457
4458 static constexpr OpEffects effects = OpEffects().CanChangeControlFlow();
4460
4463 return MaybeRepVector<MaybeRegisterRepresentation::Word32()>();
4464 }
4465
4466 V<Word32> input() const { return Base::input<Word32>(0); }
4467
4469 BranchHint default_hint)
4470 : Base(input),
4471 default_hint(default_hint),
4472 cases(cases),
4474
4475 V8_EXPORT_PRIVATE void Validate(const Graph& graph) const;
4476 void PrintOptions(std::ostream& os) const;
4477 size_t hash_value(HashingStrategy strategy = HashingStrategy::kDefault) const;
4478 auto options() const { return std::tuple{cases, default_case, default_hint}; }
4479};
4480
4481template <>
4482struct fast_hash<SwitchOp::Case> {
4484 return fast_hash_combine(v.value, v.destination);
4485 }
4486};
4487
4489 switch (op.opcode) {
4490 case Opcode::kCheckException: {
4491 auto& casted = op.Cast<CheckExceptionOp>();
4492 return {casted.didnt_throw_block, casted.catch_block};
4493 }
4494 case Opcode::kGoto: {
4495 auto& casted = op.Cast<GotoOp>();
4496 return {casted.destination};
4497 }
4498 case Opcode::kBranch: {
4499 auto& casted = op.Cast<BranchOp>();
4500 return {casted.if_true, casted.if_false};
4501 }
4502 case Opcode::kReturn:
4503 case Opcode::kTailCall:
4504 case Opcode::kDeoptimize:
4505 case Opcode::kUnreachable:
4507 case Opcode::kSwitch: {
4508 auto& casted = op.Cast<SwitchOp>();
4510 for (const SwitchOp::Case& c : casted.cases) {
4511 result.push_back(c.destination);
4512 }
4513 result.push_back(casted.default_case);
4514 return result;
4515 }
4516#define NON_TERMINATOR_CASE(op) case Opcode::k##op:
4518 UNREACHABLE();
4519#undef NON_TERMINATOR_CASE
4520 }
4521}
4522
4524 const Block& block, const Graph& graph);
4525
4526// Tuples are only used to lower operations with multiple outputs.
4527// `TupleOp` should be folded away by subsequent `ProjectionOp`s.
4528struct TupleOp : OperationT<TupleOp> {
4529 static constexpr OpEffects effects = OpEffects();
4531
4536
4537 explicit TupleOp(base::Vector<const V<Any>> inputs) : Base(inputs) {}
4538
4539 template <typename Fn, typename Mapper>
4540 V8_INLINE auto Explode(Fn fn, Mapper& mapper) const {
4541 auto mapped_inputs = mapper.template Map<4>(inputs());
4542 return fn(base::VectorOf(mapped_inputs));
4543 }
4544
4545 auto options() const { return std::tuple{}; }
4546};
4547
4548// For operations that produce multiple results, we use `ProjectionOp` to
4549// distinguish them.
4550struct ProjectionOp : FixedArityOperationT<1, ProjectionOp> {
4551 uint16_t index;
4553
4554 static constexpr OpEffects effects = OpEffects();
4556 return base::VectorOf(&rep, 1);
4557 }
4558
4563
4564 V<Any> input() const { return Base::input<Any>(0); }
4565
4566 ProjectionOp(V<Any> input, uint16_t index, RegisterRepresentation rep)
4567 : Base(input), index(index), rep(rep) {}
4568
4569 void Validate(const Graph& graph) const {
4570#ifdef DEBUG
4571 ValidateOpInputRep(graph, input(), rep, this, index);
4572#endif // DEBUG
4573 }
4574 auto options() const { return std::tuple{index, rep}; }
4575};
4576
4578 : FixedArityOperationT<1, CheckTurboshaftTypeOfOp> {
4582
4583 static constexpr OpEffects effects = OpEffects()
4588
4591 return InputsRepFactory::SingleRep(rep);
4592 }
4593
4594 OpIndex input() const { return Base::input(0); }
4595
4597 bool successful)
4598 : Base(input), rep(rep), type(std::move(type)), successful(successful) {}
4599
4600 auto options() const { return std::tuple{rep, type, successful}; }
4601};
4602
4603struct ObjectIsOp : FixedArityOperationT<1, ObjectIsOp> {
4604 enum class Kind : uint8_t {
4606 kBigInt,
4607 kBigInt64,
4608 kCallable,
4609 kConstructor,
4610 kDetectableCallable,
4612 kNonCallable,
4613 kNumber,
4614 kNumberFitsInt32,
4615 kNumberOrBigInt,
4616 kReceiver,
4618 kSmi,
4619 kString,
4621 kSymbol,
4622 kUndetectable,
4623 };
4624 enum class InputAssumptions : uint8_t {
4625 kNone,
4627 kBigInt,
4628 };
4631
4632 // All type checks performed by this operator are regarding immutable
4633 // properties. Therefore, it can be considered pure. Input assumptions,
4634 // howerever, can rely on being scheduled after checks.
4635 static constexpr OpEffects effects =
4638 return RepVector<RegisterRepresentation::Word32()>();
4639 }
4640
4643 return MaybeRepVector<MaybeRegisterRepresentation::Tagged()>();
4644 }
4645
4646 V<Object> input() const { return Base::input<Object>(0); }
4647
4648 ObjectIsOp(V<Object> input, Kind kind, InputAssumptions input_assumptions)
4649 : Base(input), kind(kind), input_assumptions(input_assumptions) {}
4650
4651 auto options() const { return std::tuple{kind, input_assumptions}; }
4652};
4653V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
4655V8_EXPORT_PRIVATE std::ostream& operator<<(
4656 std::ostream& os, ObjectIsOp::InputAssumptions input_assumptions);
4657
4658enum class NumericKind : uint8_t {
4660 kFinite,
4661 kInteger,
4663 kInt32,
4664 kSmi,
4665 kMinusZero,
4666 kNaN,
4667};
4668V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os, NumericKind kind);
4669
4670struct Float64IsOp : FixedArityOperationT<1, Float64IsOp> {
4672
4674
4675 static constexpr OpEffects effects = OpEffects();
4677 return RepVector<RegisterRepresentation::Word32()>();
4678 }
4679
4682 return MaybeRepVector<MaybeRegisterRepresentation::Float64()>();
4683 }
4684
4685 V<Float64> input() const { return Base::input<Float64>(0); }
4686
4687 auto options() const { return std::tuple{kind}; }
4688};
4689
4691 : FixedArityOperationT<1, ObjectIsNumericValueOp> {
4694
4696 FloatRepresentation input_rep)
4697 : Base(input), kind(kind), input_rep(input_rep) {}
4698
4699 // Heap numbers are immutable, so reading from them is pure. We might rely on
4700 // checks to assume that the input is a heap number.
4701 static constexpr OpEffects effects =
4704 return RepVector<RegisterRepresentation::Word32()>();
4705 }
4706
4709 return MaybeRepVector<MaybeRegisterRepresentation::Tagged()>();
4710 }
4711
4712 V<Object> input() const { return Base::input<Object>(0); }
4713
4714 auto options() const { return std::tuple{kind, input_rep}; }
4715};
4716
4717struct ConvertOp : FixedArityOperationT<1, ConvertOp> {
4718 enum class Kind : uint8_t {
4719 kObject,
4720 kBoolean,
4721 kNumber,
4723 kPlainPrimitive,
4724 kString,
4725 kSmi,
4726 };
4729
4730 // All properties/values we read are immutable.
4731 static constexpr OpEffects effects =
4732 OpEffects()
4733 // We only allocate identityless primitives here.
4735 // We might use preceding checks to ensure the input has the right
4736 // type.
4740 return RepVector<RegisterRepresentation::Tagged()>();
4741 }
4742
4745 return MaybeRepVector<MaybeRegisterRepresentation::Tagged()>();
4746 }
4747
4748 V<Object> input() const { return Base::input<Object>(0); }
4749
4750 ConvertOp(V<Object> input, Kind from, Kind to)
4751 : Base(input), from(from), to(to) {}
4752
4753 auto options() const { return std::tuple{from, to}; }
4754};
4755V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
4757
4759 : FixedArityOperationT<1, ConvertUntaggedToJSPrimitiveOp> {
4760 enum class JSPrimitiveKind : uint8_t {
4761 kBigInt,
4762 kBoolean,
4763 kHeapNumber,
4764 kHeapNumberOrUndefined,
4765 kNumber,
4766 kSmi,
4767 kString,
4768 };
4769 enum class InputInterpretation : uint8_t {
4770 kSigned,
4771 kUnsigned,
4772 kCharCode,
4773 kCodePoint,
4774 };
4779
4780 // The input is untagged and the results are identityless primitives.
4781 static constexpr OpEffects effects = OpEffects().CanAllocateWithoutIdentity();
4783 return RepVector<RegisterRepresentation::Tagged()>();
4784 }
4785
4788 return InputsRepFactory::SingleRep(input_rep);
4789 }
4790
4791 V<Untagged> input() const { return Base::input<Untagged>(0); }
4792
4794 RegisterRepresentation input_rep,
4795 InputInterpretation input_interpretation,
4796 CheckForMinusZeroMode minus_zero_mode)
4797 : Base(input),
4798 kind(kind),
4799 input_rep(input_rep),
4800 input_interpretation(input_interpretation),
4801 minus_zero_mode(minus_zero_mode) {}
4802
4803 void Validate(const Graph& graph) const {
4804 switch (kind) {
4805 case JSPrimitiveKind::kBigInt:
4806 DCHECK_EQ(input_rep, RegisterRepresentation::Word64());
4807 DCHECK_EQ(minus_zero_mode,
4808 CheckForMinusZeroMode::kDontCheckForMinusZero);
4809 break;
4810 case JSPrimitiveKind::kBoolean:
4811 DCHECK_EQ(input_rep, RegisterRepresentation::Word32());
4812 DCHECK_EQ(minus_zero_mode,
4813 CheckForMinusZeroMode::kDontCheckForMinusZero);
4814 break;
4815 case JSPrimitiveKind::kNumber:
4816 case JSPrimitiveKind::kHeapNumber:
4818 minus_zero_mode == CheckForMinusZeroMode::kCheckForMinusZero,
4819 input_rep == RegisterRepresentation::Float64());
4820 break;
4821 case JSPrimitiveKind::kHeapNumberOrUndefined:
4823 minus_zero_mode == CheckForMinusZeroMode::kDontCheckForMinusZero,
4824 input_rep == RegisterRepresentation::Float64());
4825 break;
4826 case JSPrimitiveKind::kSmi:
4827 DCHECK_EQ(input_rep, WordRepresentation::Word32());
4828 DCHECK_EQ(minus_zero_mode,
4829 CheckForMinusZeroMode::kDontCheckForMinusZero);
4830 break;
4831 case JSPrimitiveKind::kString:
4832 DCHECK_EQ(input_rep, WordRepresentation::Word32());
4833 DCHECK_EQ(input_interpretation,
4834 any_of(InputInterpretation::kCharCode,
4835 InputInterpretation::kCodePoint));
4836 break;
4837 }
4838 }
4839
4840 auto options() const {
4841 return std::tuple{kind, input_rep, input_interpretation, minus_zero_mode};
4842 }
4843};
4844V8_EXPORT_PRIVATE std::ostream& operator<<(
4846
4848 : FixedArityOperationT<2, ConvertUntaggedToJSPrimitiveOrDeoptOp> {
4849 enum class JSPrimitiveKind : uint8_t {
4850 kSmi,
4851 };
4852 enum class InputInterpretation : uint8_t {
4853 kSigned,
4854 kUnsigned,
4855 };
4860
4861 // We currently only convert Word representations to Smi or deopt. We need to
4862 // change the effects if we add more kinds.
4863 static constexpr OpEffects effects = OpEffects().CanDeopt();
4865 return RepVector<RegisterRepresentation::Tagged()>();
4866 }
4867
4870 return InputsRepFactory::SingleRep(input_rep);
4871 }
4872
4873 V<Untagged> input() const { return Base::input<Untagged>(0); }
4874 V<FrameState> frame_state() const { return Base::input<FrameState>(1); }
4875
4877 V<Untagged> input, V<FrameState> frame_state, JSPrimitiveKind kind,
4878 RegisterRepresentation input_rep,
4879 InputInterpretation input_interpretation, const FeedbackSource& feedback)
4880 : Base(input, frame_state),
4881 kind(kind),
4882 input_rep(input_rep),
4883 input_interpretation(input_interpretation),
4884 feedback(feedback) {}
4885
4886 void Validate(const Graph& graph) const {
4887 DCHECK(Get(graph, frame_state()).Is<FrameStateOp>());
4888 }
4889
4890 auto options() const {
4891 return std::tuple{kind, input_rep, input_interpretation, feedback};
4892 }
4893};
4894V8_EXPORT_PRIVATE std::ostream& operator<<(
4895 std::ostream& os,
4897V8_EXPORT_PRIVATE std::ostream& operator<<(
4899 input_interpretation);
4900
4902 : FixedArityOperationT<1, ConvertJSPrimitiveToUntaggedOp> {
4903 enum class UntaggedKind : uint8_t {
4904 kInt32,
4905 kInt64,
4906 kUint32,
4907 kBit,
4908 kFloat64,
4909 };
4910 enum class InputAssumptions : uint8_t {
4911 kBoolean,
4912 kSmi,
4914 kPlainPrimitive,
4915 };
4918
4919 // This operation can read memory, but only immutable aspects, so it counts as
4920 // pure.
4921 static constexpr OpEffects effects =
4922 OpEffects()
4923 // We might rely on preceding checks to avoid deopts.
4926 switch (kind) {
4927 case UntaggedKind::kInt32:
4928 case UntaggedKind::kUint32:
4929 case UntaggedKind::kBit:
4930 return RepVector<RegisterRepresentation::Word32()>();
4931 case UntaggedKind::kInt64:
4932 return RepVector<RegisterRepresentation::Word64()>();
4933 case UntaggedKind::kFloat64:
4934 return RepVector<RegisterRepresentation::Float64()>();
4935 }
4936 }
4937
4940 return MaybeRepVector<MaybeRegisterRepresentation::Tagged()>();
4941 }
4942
4943 V<JSPrimitive> input() const { return Base::input<JSPrimitive>(0); }
4944
4946 InputAssumptions input_assumptions)
4947 : Base(input), kind(kind), input_assumptions(input_assumptions) {}
4948
4949 auto options() const { return std::tuple{kind, input_assumptions}; }
4950};
4951V8_EXPORT_PRIVATE std::ostream& operator<<(
4953V8_EXPORT_PRIVATE std::ostream& operator<<(
4954 std::ostream& os,
4956
4958 : FixedArityOperationT<2, ConvertJSPrimitiveToUntaggedOrDeoptOp> {
4959 enum class UntaggedKind : uint8_t {
4960 kInt32,
4962 kInt64,
4963 kFloat64,
4965 };
4966 enum class JSPrimitiveKind : uint8_t {
4968 kNumber,
4971 kNumberOrString,
4972 kSmi,
4973 };
4978
4979 // This operation can read memory, but only immutable aspects, so it counts as
4980 // pure.
4981 static constexpr OpEffects effects = OpEffects().CanDeopt();
4983 switch (to_kind) {
4984 case UntaggedKind::kInt32:
4985 return RepVector<RegisterRepresentation::Word32()>();
4986 case UntaggedKind::kAdditiveSafeInteger:
4987 case UntaggedKind::kInt64:
4988 return RepVector<RegisterRepresentation::Word64()>();
4989 case UntaggedKind::kFloat64:
4990 return RepVector<RegisterRepresentation::Float64()>();
4991 case UntaggedKind::kArrayIndex:
4992 return Is64() ? RepVector<RegisterRepresentation::Word64()>()
4993 : RepVector<RegisterRepresentation::Word32()>();
4994 }
4995 }
4996
4999 return MaybeRepVector<MaybeRegisterRepresentation::Tagged()>();
5000 }
5001
5002 V<Object> input() const { return Base::input<Object>(0); }
5003 V<FrameState> frame_state() const { return Base::input<FrameState>(1); }
5004
5006 V<FrameState> frame_state,
5007 JSPrimitiveKind from_kind,
5008 UntaggedKind to_kind,
5009 CheckForMinusZeroMode minus_zero_mode,
5010 const FeedbackSource& feedback)
5011 : Base(input, frame_state),
5012 from_kind(from_kind),
5013 to_kind(to_kind),
5014 minus_zero_mode(minus_zero_mode),
5015 feedback(feedback) {}
5016 void Validate(const Graph& graph) const {
5017 DCHECK(Get(graph, frame_state()).Is<FrameStateOp>());
5018 }
5019
5020 auto options() const {
5021 return std::tuple{from_kind, to_kind, minus_zero_mode, feedback};
5022 }
5023};
5024V8_EXPORT_PRIVATE std::ostream& operator<<(
5025 std::ostream& os,
5027V8_EXPORT_PRIVATE std::ostream& operator<<(
5029
5031 : FixedArityOperationT<1, TruncateJSPrimitiveToUntaggedOp> {
5032 enum class UntaggedKind : uint8_t {
5033 kInt32,
5034 kInt64,
5035 kBit,
5036 };
5037 enum class InputAssumptions : uint8_t {
5038 kBigInt,
5041 kObject,
5042 };
5045
5046 // This operation can read memory, but only immutable aspects, so it counts as
5047 // pure.
5048 static constexpr OpEffects effects =
5049 OpEffects()
5050 // We might rely on preceding checks to ensure the input type.
5053 switch (kind) {
5054 case UntaggedKind::kInt32:
5055 case UntaggedKind::kBit:
5056 return RepVector<RegisterRepresentation::Word32()>();
5057 case UntaggedKind::kInt64:
5058 return RepVector<RegisterRepresentation::Word64()>();
5059 }
5060 }
5061
5064 return MaybeRepVector<MaybeRegisterRepresentation::Tagged()>();
5065 }
5066
5067 V<JSPrimitive> input() const { return Base::input<JSPrimitive>(0); }
5068
5070 InputAssumptions input_assumptions)
5071 : Base(input), kind(kind), input_assumptions(input_assumptions) {}
5072
5073 auto options() const { return std::tuple{kind, input_assumptions}; }
5074};
5075V8_EXPORT_PRIVATE std::ostream& operator<<(
5077V8_EXPORT_PRIVATE std::ostream& operator<<(
5078 std::ostream& os,
5080
5082 : FixedArityOperationT<2, TruncateJSPrimitiveToUntaggedOrDeoptOp> {
5083 enum class UntaggedKind : uint8_t {
5084 kInt32,
5085 };
5091
5092 static constexpr OpEffects effects = OpEffects().CanDeopt();
5094 switch (kind) {
5095 case UntaggedKind::kInt32:
5096 return RepVector<RegisterRepresentation::Word32()>();
5097 }
5098 }
5099
5102 return MaybeRepVector<MaybeRegisterRepresentation::Tagged()>();
5103 }
5104
5105 V<JSPrimitive> input() const { return Base::input<JSPrimitive>(0); }
5106 V<FrameState> frame_state() const { return Base::input<FrameState>(1); }
5107
5109 V<FrameState> frame_state,
5111 InputRequirement input_requirement,
5112 const FeedbackSource& feedback)
5113 : Base(input, frame_state),
5114 kind(kind),
5115 input_requirement(input_requirement),
5116 feedback(feedback) {}
5117 void Validate(const Graph& graph) const {
5118 DCHECK(Get(graph, frame_state()).Is<FrameStateOp>());
5119 }
5120
5121 auto options() const { return std::tuple{kind, input_requirement, feedback}; }
5122};
5123V8_EXPORT_PRIVATE std::ostream& operator<<(
5124 std::ostream& os,
5126
5128 : FixedArityOperationT<3, ConvertJSPrimitiveToObjectOp> {
5130
5131 static constexpr OpEffects effects = OpEffects().CanCallAnything();
5133 return RepVector<RegisterRepresentation::Tagged()>();
5134 }
5135
5138 return MaybeRepVector<MaybeRegisterRepresentation::Tagged(),
5139 MaybeRegisterRepresentation::Tagged()>();
5140 }
5141
5142 V<JSPrimitive> value() const { return Base::input<JSPrimitive>(0); }
5143 V<Context> native_context() const { return Base::input<Context>(1); }
5145 return Base::input<JSGlobalProxy>(2);
5146 }
5147
5149 V<JSGlobalProxy> global_proxy,
5151 : Base(value, native_context, global_proxy), mode(mode) {}
5152
5153
5154 auto options() const { return std::tuple{mode}; }
5155};
5156
5157struct NewConsStringOp : FixedArityOperationT<3, NewConsStringOp> {
5158 static constexpr OpEffects effects =
5159 OpEffects()
5160 // Strings are conceptually immutable and don't have identity.
5162 // We might rely on preceding checks to ensure the input is a string
5163 // and on their combined length being between ConsString::kMinLength
5164 // and ConsString::kMaxLength.
5167 return RepVector<RegisterRepresentation::Tagged()>();
5168 }
5169
5172 return MaybeRepVector<MaybeRegisterRepresentation::Word32(),
5173 MaybeRegisterRepresentation::Tagged(),
5174 MaybeRegisterRepresentation::Tagged()>();
5175 }
5176
5177 V<Word32> length() const { return Base::input<Word32>(0); }
5178 V<String> first() const { return Base::input<String>(1); }
5179 V<String> second() const { return Base::input<String>(2); }
5180
5182 : Base(length, first, second) {}
5183
5184 auto options() const { return std::tuple{}; }
5185};
5186
5187struct NewArrayOp : FixedArityOperationT<1, NewArrayOp> {
5188 enum class Kind : uint8_t {
5189 kDouble,
5190 kObject,
5191 };
5194
5195 static constexpr OpEffects effects =
5196 OpEffects()
5197 // Allocate the result, which has identity.
5198 .CanAllocate()
5199 // We might have checks to ensure the array length is valid and not
5200 // too big.
5203 return RepVector<RegisterRepresentation::Tagged()>();
5204 }
5205
5208 return MaybeRepVector<MaybeRegisterRepresentation::WordPtr()>();
5209 }
5210
5211 OpIndex length() const { return Base::input(0); }
5212
5213 NewArrayOp(OpIndex length, Kind kind, AllocationType allocation_type)
5214 : Base(length), kind(kind), allocation_type(allocation_type) {}
5215
5216 auto options() const { return std::tuple{kind, allocation_type}; }
5217};
5218V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
5220
5221struct DoubleArrayMinMaxOp : FixedArityOperationT<1, DoubleArrayMinMaxOp> {
5222 enum class Kind : uint8_t {
5223 kMin,
5224 kMax,
5225 };
5227
5228 static constexpr OpEffects effects =
5229 OpEffects()
5230 // Read the array contents.
5232 // Allocate the HeapNumber result.
5234 // We might depend on checks to ensure the input is an array.
5237 return RepVector<RegisterRepresentation::Tagged()>();
5238 }
5239
5242 return MaybeRepVector<MaybeRegisterRepresentation::Tagged()>();
5243 }
5244
5245 V<JSArray> array() const { return Base::input<JSArray>(0); }
5246
5248
5249 auto options() const { return std::tuple{kind}; }
5250};
5251V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
5253
5254// TODO(nicohartmann@): We should consider getting rid of the LoadFieldByIndex
5255// operation.
5256struct LoadFieldByIndexOp : FixedArityOperationT<2, LoadFieldByIndexOp> {
5257 static constexpr OpEffects effects =
5258 OpEffects()
5259 // Read the possibly mutable property.
5261 // We may allocate heap number for the result.
5263 // We assume the input is an object.
5266 return RepVector<RegisterRepresentation::Tagged()>();
5267 }
5268
5271 return MaybeRepVector<MaybeRegisterRepresentation::Tagged(),
5272 MaybeRegisterRepresentation::Word32()>();
5273 }
5274
5275 OpIndex object() const { return Base::input(0); }
5276 // Index encoding (see `src/objects/field-index-inl.h`):
5277 // For efficiency, the LoadByFieldIndex instruction takes an index that is
5278 // optimized for quick access. If the property is inline, the index is
5279 // positive. If it's out-of-line, the encoded index is -raw_index - 1 to
5280 // disambiguate the zero out-of-line index from the zero inobject case.
5281 // The index itself is shifted up by one bit, the lower-most bit
5282 // signifying if the field is a mutable double box (1) or not (0).
5283 OpIndex index() const { return Base::input(1); }
5284
5285 LoadFieldByIndexOp(OpIndex object, OpIndex index) : Base(object, index) {}
5286
5287 auto options() const { return std::tuple{}; }
5288};
5289
5290struct DebugBreakOp : FixedArityOperationT<0, DebugBreakOp> {
5291 // Prevent any reordering.
5292 static constexpr OpEffects effects = OpEffects().CanDeopt();
5294
5299
5301
5302 auto options() const { return std::tuple{}; }
5303};
5304
5305struct DebugPrintOp : FixedArityOperationT<1, DebugPrintOp> {
5307
5308 // We just need to ensure that the debug print stays in the same block and
5309 // observes the right memory state. It doesn't actually change control flow,
5310 // but pretending so ensures the we do not remove the debug print even though
5311 // it is unused. We assume that the debug print doesn't affect memory so that
5312 // the scheduling of loads is not affected.
5313 static constexpr OpEffects effects = OpEffects()
5316 .CanReadMemory()
5319
5322 return InputsRepFactory::SingleRep(rep);
5323 }
5324
5325 OpIndex input() const { return Base::input(0); }
5326
5328 : Base(input), rep(rep) {}
5329
5330 auto options() const { return std::tuple{rep}; }
5331};
5332
5333struct BigIntBinopOp : FixedArityOperationT<3, BigIntBinopOp> {
5334 enum class Kind : uint8_t {
5335 kAdd,
5336 kSub,
5337 kMul,
5338 kDiv,
5339 kMod,
5340 kBitwiseAnd,
5341 kBitwiseOr,
5342 kBitwiseXor,
5343 kShiftLeft,
5344 kShiftRightArithmetic,
5345 };
5347
5348 // These operations can deopt (abort), allocate and read immutable data.
5349 static constexpr OpEffects effects =
5350 OpEffects()
5351 // Allocate the resulting BigInt, which does not have identity.
5353 .CanDeopt();
5355 return RepVector<RegisterRepresentation::Tagged()>();
5356 }
5357
5360 return MaybeRepVector<MaybeRegisterRepresentation::Tagged(),
5361 MaybeRegisterRepresentation::Tagged()>();
5362 }
5363
5364 V<BigInt> left() const { return Base::input<BigInt>(0); }
5365 V<BigInt> right() const { return Base::input<BigInt>(1); }
5366 V<FrameState> frame_state() const { return Base::input<FrameState>(2); }
5367
5369 Kind kind)
5370 : Base(left, right, frame_state), kind(kind) {}
5371 void Validate(const Graph& graph) const {
5372 DCHECK(Get(graph, frame_state()).Is<FrameStateOp>());
5373 }
5374
5375 auto options() const { return std::tuple{kind}; }
5376};
5377V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
5379
5380struct BigIntComparisonOp : FixedArityOperationT<2, BigIntComparisonOp> {
5381 enum class Kind : uint8_t {
5382 kEqual,
5383 kLessThan,
5384 kLessThanOrEqual,
5385 };
5387
5388 static constexpr OpEffects effects =
5389 OpEffects()
5390 // We rely on the inputs having BigInt type.
5393 return RepVector<RegisterRepresentation::Tagged()>();
5394 }
5395
5398 return MaybeRepVector<MaybeRegisterRepresentation::Tagged(),
5399 MaybeRegisterRepresentation::Tagged()>();
5400 }
5401
5402 static bool IsCommutative(Kind kind) { return kind == Kind::kEqual; }
5403
5404 V<BigInt> left() const { return Base::input<BigInt>(0); }
5405 V<BigInt> right() const { return Base::input<BigInt>(1); }
5406
5408 : Base(left, right), kind(kind) {}
5409
5410
5411 auto options() const { return std::tuple{kind}; }
5412};
5413V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
5415
5416struct BigIntUnaryOp : FixedArityOperationT<1, BigIntUnaryOp> {
5417 enum class Kind : uint8_t {
5418 kNegate,
5419 };
5421
5422 static constexpr OpEffects effects =
5423 OpEffects()
5424 // BigInt content is immutable, the allocated result does not have
5425 // identity.
5427 // We rely on the input being a BigInt.
5430 return RepVector<RegisterRepresentation::Tagged()>();
5431 }
5432
5435 return MaybeRepVector<MaybeRegisterRepresentation::Tagged()>();
5436 }
5437
5438 V<BigInt> input() const { return Base::input<BigInt>(0); }
5439
5441
5442
5443 auto options() const { return std::tuple{kind}; }
5444};
5445
5446V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
5448
5449struct LoadRootRegisterOp : FixedArityOperationT<0, LoadRootRegisterOp> {
5450 static constexpr OpEffects effects = OpEffects();
5452 return RepVector<RegisterRepresentation::WordPtr()>();
5453 }
5454
5459
5461 std::tuple<> options() const { return {}; }
5462};
5463
5464struct StringAtOp : FixedArityOperationT<2, StringAtOp> {
5465 enum class Kind : uint8_t {
5466 kCharCode,
5467 kCodePoint,
5468 };
5470
5471 static constexpr OpEffects effects =
5472 // String content is immutable, so this operation is pure.
5473 OpEffects()
5474 // We rely on the input being a string.
5477 return RepVector<RegisterRepresentation::Word32()>();
5478 }
5479
5482 return MaybeRepVector<MaybeRegisterRepresentation::Tagged(),
5483 MaybeRegisterRepresentation::WordPtr()>();
5484 }
5485
5486 V<String> string() const { return Base::input<String>(0); }
5487 V<WordPtr> position() const { return Base::input<WordPtr>(1); }
5488
5491
5492
5493 auto options() const { return std::tuple{kind}; }
5494};
5495V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
5497
5498#ifdef V8_INTL_SUPPORT
5499struct StringToCaseIntlOp : FixedArityOperationT<1, StringToCaseIntlOp> {
5500 enum class Kind : uint8_t {
5501 kLower,
5502 kUpper,
5503 };
5504 Kind kind;
5505
5506 static constexpr OpEffects effects =
5507 OpEffects()
5508 // String content is immutable, the allocated result does not have
5509 // identity.
5511 // We rely on the input being a string.
5513 base::Vector<const RegisterRepresentation> outputs_rep() const {
5514 return RepVector<RegisterRepresentation::Tagged()>();
5515 }
5516
5518 ZoneVector<MaybeRegisterRepresentation>& storage) const {
5519 return MaybeRepVector<MaybeRegisterRepresentation::Tagged()>();
5520 }
5521
5522 V<String> string() const { return Base::input<String>(0); }
5523
5524 StringToCaseIntlOp(V<String> string, Kind kind) : Base(string), kind(kind) {}
5525
5526
5527 auto options() const { return std::tuple{kind}; }
5528};
5529V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
5530 StringToCaseIntlOp::Kind kind);
5531#endif // V8_INTL_SUPPORT
5532
5533struct StringLengthOp : FixedArityOperationT<1, StringLengthOp> {
5534 static constexpr OpEffects effects =
5535 // String content is immutable, so this operation is pure.
5536 OpEffects()
5537 // We rely on the input being a string.
5540 return RepVector<RegisterRepresentation::Word32()>();
5541 }
5542
5545 return MaybeRepVector<MaybeRegisterRepresentation::Tagged()>();
5546 }
5547
5548 V<String> string() const { return Base::input<String>(0); }
5549
5550 explicit StringLengthOp(V<String> string) : Base(string) {}
5551
5552
5553 auto options() const { return std::tuple{}; }
5554};
5555
5556struct TypedArrayLengthOp : FixedArityOperationT<1, TypedArrayLengthOp> {
5558
5559 static constexpr OpEffects effects =
5560 // This operation is only triggered for constant-length JSTypedArrays, so
5561 // it's pure.
5562 OpEffects()
5563 // We rely on the input being a JSTypedArray.
5566 return RepVector<RegisterRepresentation::WordPtr()>();
5567 }
5568
5571 return MaybeRepVector<MaybeRegisterRepresentation::Tagged()>();
5572 }
5573
5574 V<JSTypedArray> typed_array() const { return Base::input<JSTypedArray>(0); }
5575
5577 ElementsKind elements_kind)
5578 : Base(typed_array), elements_kind(elements_kind) {}
5579
5580 auto options() const { return std::tuple{elements_kind}; }
5581};
5582
5583struct StringIndexOfOp : FixedArityOperationT<3, StringIndexOfOp> {
5584 static constexpr OpEffects effects =
5585 OpEffects()
5586 // String content is immutable, the allocated result does not have
5587 // identity.
5589 // We rely on the inputs being strings.
5592 return RepVector<RegisterRepresentation::Tagged()>();
5593 }
5594
5597 return MaybeRepVector<MaybeRegisterRepresentation::Tagged(),
5598 MaybeRegisterRepresentation::Tagged(),
5599 MaybeRegisterRepresentation::Tagged()>();
5600 }
5601
5602 // Search the string `search` within the string `string` starting at
5603 // `position`.
5604 V<String> string() const { return Base::input<String>(0); }
5605 V<String> search() const { return Base::input<String>(1); }
5606 V<Smi> position() const { return Base::input<Smi>(2); }
5607
5609 : Base(string, search, position) {}
5610
5611
5612 auto options() const { return std::tuple{}; }
5613};
5614
5616 : FixedArityOperationT<2, StringFromCodePointAtOp> {
5617 static constexpr OpEffects effects =
5618 OpEffects()
5619 // String content is immutable, the allocated result does not have
5620 // identity.
5622 // We rely on the input being in a certain char range.
5625 return RepVector<RegisterRepresentation::Tagged()>();
5626 }
5627
5630 return MaybeRepVector<MaybeRegisterRepresentation::Tagged(),
5631 MaybeRegisterRepresentation::WordPtr()>();
5632 }
5633
5634 V<String> string() const { return Base::input<String>(0); }
5635 V<WordPtr> index() const { return Base::input<WordPtr>(1); }
5636
5638 : Base(string, index) {}
5639
5640
5641 auto options() const { return std::tuple{}; }
5642};
5643
5644struct StringSubstringOp : FixedArityOperationT<3, StringSubstringOp> {
5645 static constexpr OpEffects effects =
5646 OpEffects()
5647 // String content is immutable, the allocated result does not have
5648 // identity.
5650 // We rely on the input being a string.
5653 return RepVector<RegisterRepresentation::Tagged()>();
5654 }
5655
5658 return MaybeRepVector<MaybeRegisterRepresentation::Tagged(),
5659 MaybeRegisterRepresentation::Word32(),
5660 MaybeRegisterRepresentation::Word32()>();
5661 }
5662
5663 V<String> string() const { return Base::input<String>(0); }
5664 V<Word32> start() const { return Base::input<Word32>(1); }
5665 V<Word32> end() const { return Base::input<Word32>(2); }
5666
5669
5670
5671 auto options() const { return std::tuple{}; }
5672};
5673
5674struct StringConcatOp : FixedArityOperationT<3, StringConcatOp> {
5675 static constexpr OpEffects effects =
5676 OpEffects()
5677 // String content is immutable, the allocated result does not have
5678 // identity.
5680 // We rely on the inputs being strings.
5683 return RepVector<RegisterRepresentation::Tagged()>();
5684 }
5685
5688 return MaybeRepVector<MaybeRegisterRepresentation::Tagged(),
5689 MaybeRegisterRepresentation::Tagged(),
5690 MaybeRegisterRepresentation::Tagged()>();
5691 }
5692
5693 V<Smi> length() const { return Base::input<Smi>(0); }
5694 V<String> left() const { return Base::input<String>(1); }
5695 V<String> right() const { return Base::input<String>(2); }
5696
5698 : Base(length, left, right) {}
5699
5700
5701 auto options() const { return std::tuple{}; }
5702};
5703
5704struct StringComparisonOp : FixedArityOperationT<2, StringComparisonOp> {
5705 enum class Kind : uint8_t {
5706 kEqual,
5707 kLessThan,
5708 kLessThanOrEqual,
5709 };
5711
5712 static constexpr OpEffects effects =
5713 // String content is immutable, so the operation is pure.
5714 OpEffects()
5715 // We rely on the input being strings.
5718 return RepVector<RegisterRepresentation::Tagged()>();
5719 }
5720
5723 return MaybeRepVector<MaybeRegisterRepresentation::Tagged(),
5724 MaybeRegisterRepresentation::Tagged()>();
5725 }
5726
5727 static bool IsCommutative(Kind kind) { return kind == Kind::kEqual; }
5728
5729 V<String> left() const { return Base::input<String>(0); }
5730 V<String> right() const { return Base::input<String>(1); }
5731
5733 : Base(left, right), kind(kind) {}
5734
5735
5736 auto options() const { return std::tuple{kind}; }
5737};
5738V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
5740
5741struct ArgumentsLengthOp : FixedArityOperationT<0, ArgumentsLengthOp> {
5742 enum class Kind : uint8_t {
5743 kArguments,
5744 kRest,
5745 };
5747 int formal_parameter_count =
5748 0; // This field is unused for kind == kArguments.
5749
5750 static constexpr OpEffects effects = OpEffects();
5752 return RepVector<RegisterRepresentation::Tagged()>();
5753 }
5754
5759
5760 explicit ArgumentsLengthOp(Kind kind, int formal_parameter_count)
5761 : Base(), kind(kind), formal_parameter_count(formal_parameter_count) {
5762 DCHECK_IMPLIES(kind == Kind::kArguments, formal_parameter_count == 0);
5763 }
5764
5765
5766 auto options() const { return std::tuple{kind, formal_parameter_count}; }
5767};
5768V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
5770
5772 : FixedArityOperationT<1, NewArgumentsElementsOp> {
5775
5776 static constexpr OpEffects effects =
5777 OpEffects()
5778 // Allocate the fixed array, which has identity.
5779 .CanAllocate()
5780 // Do not move the allocation before checks/branches.
5783 return RepVector<RegisterRepresentation::Tagged()>();
5784 }
5785
5788 return MaybeRepVector<MaybeRegisterRepresentation::Tagged()>();
5789 }
5790
5791 OpIndex arguments_count() const { return Base::input(0); }
5792
5794 int formal_parameter_count)
5795 : Base(arguments_count),
5796 type(type),
5797 formal_parameter_count(formal_parameter_count) {}
5798
5799
5800 auto options() const { return std::tuple{type, formal_parameter_count}; }
5801};
5802
5804 ExternalArrayType array_type) {
5805 switch (array_type) {
5806 case kExternalInt8Array:
5814 return RegisterRepresentation::Word32();
5816 return RegisterRepresentation::Float32();
5818 return RegisterRepresentation::Float64();
5821 return RegisterRepresentation::Word64();
5822 }
5823}
5824
5827 static constexpr std::array<RegisterRepresentation, 6> table{
5828 RegisterRepresentation::Word32(), RegisterRepresentation::Word64(),
5829 RegisterRepresentation::Float32(), RegisterRepresentation::Float64(),
5830 RegisterRepresentation::Tagged(), RegisterRepresentation::Compressed()};
5831 return base::VectorOf(&table[static_cast<size_t>(rep.value())], 1);
5832}
5833
5834struct LoadTypedElementOp : FixedArityOperationT<4, LoadTypedElementOp> {
5836
5837 static constexpr OpEffects effects =
5838 OpEffects()
5839 // We read mutable memory.
5840 .CanReadMemory()
5841 // We rely on the input type and a valid index.
5846
5849 return MaybeRepVector<MaybeRegisterRepresentation::Tagged(),
5850 MaybeRegisterRepresentation::Tagged(),
5851 MaybeRegisterRepresentation::WordPtr(),
5852 MaybeRegisterRepresentation::WordPtr()>();
5853 }
5854
5855 OpIndex buffer() const { return Base::input(0); }
5856 OpIndex base() const { return Base::input(1); }
5857 OpIndex external() const { return Base::input(2); }
5858 OpIndex index() const { return Base::input(3); }
5859
5861 OpIndex index, ExternalArrayType array_type)
5862 : Base(buffer, base, external, index), array_type(array_type) {}
5863
5864
5865 auto options() const { return std::tuple{array_type}; }
5866};
5867
5868struct LoadDataViewElementOp : FixedArityOperationT<4, LoadDataViewElementOp> {
5870
5871 static constexpr OpEffects effects = OpEffects()
5872 // We read mutable memory.
5873 .CanReadMemory()
5874 // We rely on the input type.
5879
5882 return MaybeRepVector<MaybeRegisterRepresentation::Tagged(),
5883 MaybeRegisterRepresentation::Tagged(),
5884 MaybeRegisterRepresentation::WordPtr(),
5885 MaybeRegisterRepresentation::Word32()>();
5886 }
5887
5888 OpIndex object() const { return Base::input(0); }
5889 OpIndex storage() const { return Base::input(1); }
5890 OpIndex index() const { return Base::input(2); }
5891 OpIndex is_little_endian() const { return Base::input(3); }
5892
5894 OpIndex is_little_endian,
5895 ExternalArrayType element_type)
5896 : Base(object, storage, index, is_little_endian),
5897 element_type(element_type) {}
5898
5899
5900 auto options() const { return std::tuple{element_type}; }
5901};
5902
5903struct LoadStackArgumentOp : FixedArityOperationT<2, LoadStackArgumentOp> {
5904 // Stack arguments are immutable, so reading them is pure.
5905 static constexpr OpEffects effects =
5906 OpEffects()
5907 // We rely on the input being in bounds.
5910 return RepVector<RegisterRepresentation::Tagged()>();
5911 }
5912
5915 return MaybeRepVector<MaybeRegisterRepresentation::WordPtr(),
5916 MaybeRegisterRepresentation::WordPtr()>();
5917 }
5918
5919 OpIndex base() const { return Base::input(0); }
5920 OpIndex index() const { return Base::input(1); }
5921
5923
5924
5925 auto options() const { return std::tuple{}; }
5926};
5927
5928struct StoreTypedElementOp : FixedArityOperationT<5, StoreTypedElementOp> {
5930
5931 static constexpr OpEffects effects =
5932 OpEffects()
5933 // We are reading the backing store pointer and writing into it.
5934 .CanReadMemory()
5936 // We rely on the input type and a valid index.
5939
5942 return InitVectorOf(
5943 storage,
5944 {RegisterRepresentation::Tagged(), RegisterRepresentation::Tagged(),
5945 RegisterRepresentation::WordPtr(), RegisterRepresentation::WordPtr(),
5947 }
5948
5949 OpIndex buffer() const { return Base::input(0); }
5950 OpIndex base() const { return Base::input(1); }
5951 OpIndex external() const { return Base::input(2); }
5952 OpIndex index() const { return Base::input(3); }
5953 OpIndex value() const { return Base::input(4); }
5954
5956 OpIndex index, OpIndex value,
5957 ExternalArrayType array_type)
5958 : Base(buffer, base, external, index, value), array_type(array_type) {}
5959
5960
5961 auto options() const { return std::tuple{array_type}; }
5962};
5963
5965 : FixedArityOperationT<5, StoreDataViewElementOp> {
5967
5968 static constexpr OpEffects effects =
5969 OpEffects()
5970 // We are reading the backing store pointer and writing into it.
5971 .CanReadMemory()
5973 // We rely on the input type and a valid index.
5976
5979 return InitVectorOf(
5980 storage,
5981 {RegisterRepresentation::Tagged(), RegisterRepresentation::Tagged(),
5982 RegisterRepresentation::WordPtr(),
5984 RegisterRepresentation::Word32()});
5985 }
5986
5987 OpIndex object() const { return Base::input(0); }
5988 OpIndex storage() const { return Base::input(1); }
5989 OpIndex index() const { return Base::input(2); }
5990 OpIndex value() const { return Base::input(3); }
5991 OpIndex is_little_endian() const { return Base::input(4); }
5992
5994 OpIndex value, OpIndex is_little_endian,
5995 ExternalArrayType element_type)
5996 : Base(object, storage, index, value, is_little_endian),
5997 element_type(element_type) {}
5998
5999
6000 auto options() const { return std::tuple{element_type}; }
6001};
6002
6004 : FixedArityOperationT<3, TransitionAndStoreArrayElementOp> {
6005 enum class Kind : uint8_t {
6006 kElement,
6007 kNumberElement,
6008 kOddballElement,
6009 kNonNumberElement,
6010 kSignedSmallElement,
6011 };
6015
6016 static constexpr OpEffects effects =
6017 OpEffects()
6018 // We are reading and writing mutable memory.
6021 // We rely on the input type and a valid index.
6024
6027 return InitVectorOf(
6028 storage, {RegisterRepresentation::Tagged(),
6029 RegisterRepresentation::WordPtr(), value_representation()});
6030 }
6031
6032 V<JSArray> array() const { return Base::input<JSArray>(0); }
6033 V<WordPtr> index() const { return Base::input<WordPtr>(1); }
6034 V<Any> value() const { return Base::input<Any>(2); }
6035
6037 V<Any> value, Kind kind,
6038 MaybeIndirectHandle<Map> fast_map,
6039 MaybeIndirectHandle<Map> double_map)
6040 : Base(array, index, value),
6041 kind(kind),
6042 fast_map(fast_map),
6043 double_map(double_map) {}
6044
6045
6047 switch (kind) {
6048 case Kind::kElement:
6049 case Kind::kNonNumberElement:
6050 case Kind::kOddballElement:
6051 return RegisterRepresentation::Tagged();
6052 case Kind::kNumberElement:
6053 return RegisterRepresentation::Float64();
6054 case Kind::kSignedSmallElement:
6055 return RegisterRepresentation::Word32();
6056 }
6057 }
6058
6060 HashingStrategy strategy = HashingStrategy::kDefault) const {
6061 DCHECK_EQ(strategy, HashingStrategy::kDefault);
6062 return HashWithOptions(fast_map.address(), double_map.address());
6063 }
6064
6066 return kind == other.kind && fast_map.equals(other.fast_map) &&
6067 double_map.equals(other.double_map);
6068 }
6069
6070 auto options() const { return std::tuple{kind, fast_map, double_map}; }
6071};
6072
6073V8_EXPORT_PRIVATE std::ostream& operator<<(
6075
6076struct CompareMapsOp : OperationT<CompareMapsOp> {
6078
6079 static constexpr OpEffects effects = OpEffects().CanReadHeapMemory();
6081 return RepVector<RegisterRepresentation::Word32()>();
6082 }
6083
6086 return MaybeRepVector<MaybeRegisterRepresentation::Tagged()>();
6087 }
6088
6089 V<HeapObject> heap_object() const { return Base::input<HeapObject>(0); }
6090
6092 return input_count > 1 ? input<Map>(1) : OptionalV<Map>::Nullopt();
6093 }
6094
6096 ZoneRefSet<Map> maps)
6097 : Base(1 + map.valid()), maps(std::move(maps)) {
6098 input(0) = heap_object;
6099 if (map.valid()) {
6100 input(1) = map.value();
6101 }
6102 }
6103
6104
6105 auto options() const { return std::tuple{maps}; }
6106 void PrintOptions(std::ostream& os) const;
6107
6108 template <typename Fn, typename Mapper>
6109 V8_INLINE auto Explode(Fn fn, Mapper& mapper) const {
6110 return fn(mapper.Map(heap_object()), mapper.Map(map()), maps);
6111 }
6112
6113 static CompareMapsOp& New(Graph* graph, V<HeapObject> heap_object,
6114 OptionalV<Map> map, ZoneRefSet<Map> maps) {
6115 return Base::New(graph, 1 + map.valid(), heap_object, map, maps);
6116 }
6117};
6118
6119struct CheckMapsOp : OperationT<CheckMapsOp> {
6123
6124 // TODO(tebbi): Map checks without map transitions have less effects.
6125 static constexpr OpEffects effects = OpEffects()
6127 .CanDeopt()
6131
6134 return MaybeRepVector<MaybeRegisterRepresentation::Tagged()>();
6135 }
6136
6137 V<HeapObject> heap_object() const { return Base::input<HeapObject>(0); }
6138 V<FrameState> frame_state() const { return Base::input<FrameState>(1); }
6140 return input_count > 2 ? input<Map>(2) : OptionalV<Map>::Nullopt();
6141 }
6142
6143 CheckMapsOp(V<HeapObject> heap_object, V<FrameState> frame_state,
6145 const FeedbackSource& feedback)
6146 : Base(2 + map.valid()),
6147 flags(flags),
6148 maps(std::move(maps)),
6149 feedback(feedback) {
6150 input(0) = heap_object;
6151 input(1) = frame_state;
6152 if (map.valid()) {
6153 input(2) = map.value();
6154 }
6155 }
6156
6157 void Validate(const Graph& graph) const {
6158 DCHECK(Get(graph, frame_state()).Is<FrameStateOp>());
6159 }
6160
6161 auto options() const { return std::tuple{maps, flags, feedback}; }
6162 void PrintOptions(std::ostream& os) const;
6163
6164 template <typename Fn, typename Mapper>
6165 V8_INLINE auto Explode(Fn fn, Mapper& mapper) const {
6166 return fn(mapper.Map(heap_object()), mapper.Map(frame_state()),
6167 mapper.Map(map()), maps, flags, feedback);
6168 }
6169
6170 static CheckMapsOp& New(Graph* graph, V<HeapObject> heap_object,
6171 V<FrameState> frame_state, OptionalV<Map> map,
6172 ZoneRefSet<Map> maps, CheckMapsFlags flags,
6173 const FeedbackSource& feedback) {
6174 return Base::New(graph, 2 + map.valid(), heap_object, frame_state, map,
6175 maps, flags, feedback);
6176 }
6177};
6178
6179// AssumeMaps are inserted after CheckMaps have been lowered, in order to keep
6180// map information around and easily accessible for subsequent optimization
6181// passes (Load Elimination for instance can then use those AssumeMap to
6182// determine that some objects don't alias because they have different maps).
6183struct AssumeMapOp : FixedArityOperationT<1, AssumeMapOp> {
6185 // AssumeMap should not be scheduled before the preceding CheckMaps
6186 static constexpr OpEffects effects = OpEffects()
6191
6194 return MaybeRepVector<MaybeRegisterRepresentation::Tagged()>();
6195 }
6196
6197 V<HeapObject> heap_object() const { return Base::input<HeapObject>(0); }
6198
6200 : Base(heap_object), maps(std::move(maps)) {}
6201
6202
6203 auto options() const { return std::tuple{maps}; }
6204 void PrintOptions(std::ostream& os) const;
6205};
6206
6207struct CheckedClosureOp : FixedArityOperationT<2, CheckedClosureOp> {
6209
6210 // We only check immutable aspects of the incoming value.
6211 static constexpr OpEffects effects = OpEffects().CanDeopt();
6213 return RepVector<RegisterRepresentation::Tagged()>();
6214 }
6215
6218 return MaybeRepVector<MaybeRegisterRepresentation::Tagged()>();
6219 }
6220
6221 V<Object> input() const { return Base::input<Object>(0); }
6222 V<FrameState> frame_state() const { return Base::input<FrameState>(1); }
6223
6225 Handle<FeedbackCell> feedback_cell)
6226 : Base(input, frame_state), feedback_cell(feedback_cell) {}
6227
6228 void Validate(const Graph& graph) const {
6229 DCHECK(Get(graph, frame_state()).Is<FrameStateOp>());
6230 }
6231
6232 bool operator==(const CheckedClosureOp& other) const {
6233 return feedback_cell.address() == other.feedback_cell.address();
6234 }
6236 HashingStrategy strategy = HashingStrategy::kDefault) const {
6237 DCHECK_EQ(strategy, HashingStrategy::kDefault);
6238 return HashWithOptions(feedback_cell.address());
6239 }
6240
6241 auto options() const { return std::tuple{feedback_cell}; }
6242};
6243
6245 : FixedArityOperationT<3, CheckEqualsInternalizedStringOp> {
6246 static constexpr OpEffects effects = OpEffects().CanDeopt();
6248
6251 return MaybeRepVector<MaybeRegisterRepresentation::Tagged(),
6252 MaybeRegisterRepresentation::Tagged()>();
6253 }
6254
6255 V<Object> expected() const { return Base::input<Object>(0); }
6256 V<Object> value() const { return Base::input<Object>(1); }
6257 V<FrameState> frame_state() const { return Base::input<FrameState>(2); }
6258
6260 V<FrameState> frame_state)
6261 : Base(expected, value, frame_state) {}
6262
6263 void Validate(const Graph& graph) const {
6264 DCHECK(Get(graph, frame_state()).Is<FrameStateOp>());
6265 }
6266
6267 auto options() const { return std::tuple{}; }
6268};
6269
6270struct LoadMessageOp : FixedArityOperationT<1, LoadMessageOp> {
6271 static constexpr OpEffects effects =
6272 OpEffects()
6273 // We are reading the message from the isolate.
6276 return RepVector<RegisterRepresentation::Tagged()>();
6277 }
6278
6281 return MaybeRepVector<MaybeRegisterRepresentation::WordPtr()>();
6282 }
6283
6284 V<WordPtr> offset() const { return Base::input<WordPtr>(0); }
6285
6287
6288
6289 auto options() const { return std::tuple{}; }
6290};
6291
6292struct StoreMessageOp : FixedArityOperationT<2, StoreMessageOp> {
6293 static constexpr OpEffects effects =
6294 OpEffects()
6295 // We are writing the message in the isolate.
6298
6301 return MaybeRepVector<MaybeRegisterRepresentation::WordPtr(),
6302 MaybeRegisterRepresentation::Tagged()>();
6303 }
6304
6305 V<WordPtr> offset() const { return Base::input<WordPtr>(0); }
6306 V<Object> object() const { return Base::input<Object>(1); }
6307
6309 : Base(offset, object) {}
6310
6311
6312 auto options() const { return std::tuple{}; }
6313};
6314
6315struct SameValueOp : FixedArityOperationT<2, SameValueOp> {
6316 enum class Mode : uint8_t {
6317 kSameValue,
6318 kSameValueNumbersOnly,
6319 };
6321
6322 static constexpr OpEffects effects =
6323 OpEffects()
6324 // We might depend on the inputs being numbers.
6327 return RepVector<RegisterRepresentation::Tagged()>();
6328 }
6329
6332 return MaybeRepVector<MaybeRegisterRepresentation::Tagged(),
6333 MaybeRegisterRepresentation::Tagged()>();
6334 }
6335
6336 V<Object> left() const { return Base::input<Object>(0); }
6337 V<Object> right() const { return Base::input<Object>(1); }
6338
6340 : Base(left, right), mode(mode) {}
6341
6342
6343 auto options() const { return std::tuple{mode}; }
6344};
6345V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
6346 SameValueOp::Mode mode);
6347
6348struct Float64SameValueOp : FixedArityOperationT<2, Float64SameValueOp> {
6349 static constexpr OpEffects effects = OpEffects();
6351 return RepVector<RegisterRepresentation::Word32()>();
6352 }
6353
6356 return MaybeRepVector<MaybeRegisterRepresentation::Float64(),
6357 MaybeRegisterRepresentation::Float64()>();
6358 }
6359
6360 V<Float64> left() const { return Base::input<Float64>(0); }
6361 V<Float64> right() const { return Base::input<Float64>(1); }
6362
6363 Float64SameValueOp(V<Float64> left, V<Float64> right) : Base(left, right) {}
6364
6365
6366 auto options() const { return std::tuple{}; }
6367};
6368
6369struct FastApiCallParameters : public NON_EXPORTED_BASE(ZoneObject) {
6371
6372 const CFunctionInfo* c_signature() const { return c_function.signature; }
6373
6375 : c_function(c_function) {}
6376
6378 Zone* graph_zone) {
6379 return graph_zone->New<FastApiCallParameters>(c_function);
6380 }
6381};
6382
6383struct FastApiCallOp : OperationT<FastApiCallOp> {
6384 static constexpr uint32_t kSuccessValue = 1;
6385 static constexpr uint32_t kFailureValue = 0;
6386
6390
6391 static constexpr OpEffects effects = OpEffects().CanCallAnything();
6392
6393 // There are three inputs that are not parameters, the frame state, the data
6394 // argument, and the context.
6395 static constexpr int kNumNonParamInputs = 3;
6396
6397 // The outputs are produced by the `DidntThrow` operation.
6399
6402 DCHECK_EQ(inputs().size(),
6403 kNumNonParamInputs + parameters->c_signature()->ArgumentCount());
6404 storage.resize(inputs().size());
6405 storage[0] = MaybeRegisterRepresentation::None();
6406 storage[1] = MaybeRegisterRepresentation::Tagged();
6407 storage[2] = MaybeRegisterRepresentation::Tagged();
6408 for (unsigned i = 0; i < parameters->c_signature()->ArgumentCount(); ++i) {
6409 storage[i + kNumNonParamInputs] = argument_representation(i);
6410 }
6411 return base::VectorOf(storage);
6412 }
6413
6415 unsigned argument_index) const {
6416 const CTypeInfo& arg_type =
6417 parameters->c_signature()->ArgumentInfo(argument_index);
6418 uint8_t flags = static_cast<uint8_t>(arg_type.GetFlags());
6420 switch (arg_type.GetSequenceType()) {
6421 case CTypeInfo::SequenceType::kScalar:
6422 if (flags & (static_cast<uint8_t>(CTypeInfo::Flags::kEnforceRangeBit) |
6423 static_cast<uint8_t>(CTypeInfo::Flags::kClampBit))) {
6424 return MaybeRegisterRepresentation::Float64();
6425 }
6426 switch (arg_type.GetType()) {
6427 case CTypeInfo::Type::kVoid:
6428 UNREACHABLE();
6429 case CTypeInfo::Type::kBool:
6430 case CTypeInfo::Type::kUint8:
6431 case CTypeInfo::Type::kInt32:
6432 case CTypeInfo::Type::kUint32:
6433 return MaybeRegisterRepresentation::Word32();
6434 case CTypeInfo::Type::kInt64:
6435 case CTypeInfo::Type::kUint64:
6436 return MaybeRegisterRepresentation::Word64();
6437 case CTypeInfo::Type::kV8Value:
6438 case CTypeInfo::Type::kApiObject:
6439 case CTypeInfo::Type::kPointer:
6440 case CTypeInfo::Type::kSeqOneByteString:
6441 return MaybeRegisterRepresentation::Tagged();
6442 case CTypeInfo::Type::kFloat32:
6443 case CTypeInfo::Type::kFloat64:
6444 return MaybeRegisterRepresentation::Float64();
6445 case CTypeInfo::Type::kAny:
6446 // As the register representation is unknown, just treat it as None
6447 // to prevent any validation.
6448 return MaybeRegisterRepresentation::None();
6449 }
6450 case CTypeInfo::SequenceType::kIsSequence:
6451 return MaybeRegisterRepresentation::Tagged();
6452 case CTypeInfo::SequenceType::kIsArrayBuffer:
6453 UNREACHABLE();
6454 }
6456 }
6457
6458 V<FrameState> frame_state() const { return input<FrameState>(0); }
6459
6460 V<Object> data_argument() const { return input<Object>(1); }
6461
6462 V<Context> context() const { return input<Context>(2); }
6463
6465 return inputs().SubVector(kNumNonParamInputs, inputs().size());
6466 }
6467
6468 FastApiCallOp(V<FrameState> frame_state, V<Object> data_argument,
6469 V<Context> context, base::Vector<const OpIndex> arguments,
6470 const FastApiCallParameters* parameters,
6472 : Base(kNumNonParamInputs + arguments.size()),
6473 parameters(parameters),
6474 out_reps(out_reps),
6475 lazy_deopt_on_throw(LazyDeoptOnThrow::kNo) {
6476 base::Vector<OpIndex> inputs = this->inputs();
6477 inputs[0] = frame_state;
6478 inputs[1] = data_argument;
6479 inputs[2] = context;
6480 inputs.SubVector(kNumNonParamInputs, kNumNonParamInputs + arguments.size())
6481 .OverwriteWith(arguments);
6482 }
6483
6484 template <typename Fn, typename Mapper>
6485 V8_INLINE auto Explode(Fn fn, Mapper& mapper) const {
6486 V<FrameState> mapped_frame_state = mapper.Map(frame_state());
6487 OpIndex mapped_data_argument = mapper.Map(data_argument());
6488 V<Context> mapped_context = mapper.Map(context());
6489 auto mapped_arguments = mapper.template Map<8>(arguments());
6490 return fn(mapped_frame_state, mapped_data_argument, mapped_context,
6491 base::VectorOf(mapped_arguments), parameters, out_reps);
6492 }
6493
6494
6496 Graph* graph, V<FrameState> frame_state, V<Object> data_argument,
6497 V<Context> context, base::Vector<const OpIndex> arguments,
6498 const FastApiCallParameters* parameters,
6500 return Base::New(graph, kNumNonParamInputs + arguments.size(), frame_state,
6501 data_argument, context, arguments, parameters, out_reps);
6502 }
6503
6504 // out_reps[0] is always word32.
6505 auto options() const {
6506 DCHECK_EQ(out_reps[0], RegisterRepresentation::Word32());
6507 return std::tuple{parameters, out_reps[1], lazy_deopt_on_throw};
6508 }
6509};
6510
6511struct RuntimeAbortOp : FixedArityOperationT<0, RuntimeAbortOp> {
6513
6514 static constexpr OpEffects effects = OpEffects().CanCallAnything();
6516
6521
6522 explicit RuntimeAbortOp(AbortReason reason) : reason(reason) {}
6523
6524
6525 auto options() const { return std::tuple{reason}; }
6526};
6527
6529 : FixedArityOperationT<2, EnsureWritableFastElementsOp> {
6530 // TODO(tebbi): Can we have more precise effects here?
6531 static constexpr OpEffects effects = OpEffects().CanCallAnything();
6533 return RepVector<RegisterRepresentation::Tagged()>();
6534 }
6535
6538 return MaybeRepVector<MaybeRegisterRepresentation::Tagged(),
6539 MaybeRegisterRepresentation::Tagged()>();
6540 }
6541
6542 OpIndex object() const { return Base::input(0); }
6543 OpIndex elements() const { return Base::input(1); }
6544
6546 : Base(object, elements) {}
6547
6548
6549 auto options() const { return std::tuple{}; }
6550};
6551
6553 : FixedArityOperationT<5, MaybeGrowFastElementsOp> {
6556
6557 // TODO(tebbi): Can we have more precise effects here?
6558 static constexpr OpEffects effects = OpEffects().CanCallAnything();
6560 return RepVector<RegisterRepresentation::Tagged()>();
6561 }
6562
6565 return MaybeRepVector<MaybeRegisterRepresentation::Tagged(),
6566 MaybeRegisterRepresentation::Tagged(),
6567 MaybeRegisterRepresentation::Word32(),
6568 MaybeRegisterRepresentation::Word32()>();
6569 }
6570
6571 V<Object> object() const { return Base::input<Object>(0); }
6572 V<Object> elements() const { return Base::input<Object>(1); }
6573 V<Word32> index() const { return Base::input<Word32>(2); }
6574 V<Word32> elements_length() const { return Base::input<Word32>(3); }
6575 V<FrameState> frame_state() const { return Base::input<FrameState>(4); }
6576
6578 V<Word32> elements_length, V<FrameState> frame_state,
6580 const FeedbackSource& feedback)
6581 : Base(object, elements, index, elements_length, frame_state),
6582 mode(mode),
6583 feedback(feedback) {}
6584
6585 void Validate(const Graph& graph) const {
6586 DCHECK(Get(graph, frame_state()).Is<FrameStateOp>());
6587 }
6588
6589 auto options() const { return std::tuple{mode, feedback}; }
6590};
6591
6593 : FixedArityOperationT<1, TransitionElementsKindOp> {
6595
6596 static constexpr OpEffects effects = OpEffects().CanCallAnything();
6598
6601 return MaybeRepVector<MaybeRegisterRepresentation::Tagged()>();
6602 }
6603
6604 OpIndex object() const { return Base::input(0); }
6605
6607 : Base(object), transition(transition) {}
6608
6609
6610 auto options() const { return std::tuple{transition}; }
6611};
6612
6614 : FixedArityOperationT<3, TransitionElementsKindOrCheckMapOp> {
6616
6617 static constexpr OpEffects effects = OpEffects().CanCallAnything();
6619
6622 return MaybeRepVector<MaybeRegisterRepresentation::Tagged()>();
6623 }
6624
6625 V<HeapObject> object() const { return Base::input<HeapObject>(0); }
6626
6628 V<HeapObject> object, V<Map> map, V<FrameState> frame_state,
6629 const ElementsTransitionWithMultipleSources& transition)
6630 : Base(object, map, frame_state), transition(transition) {}
6631
6632
6633 auto options() const { return std::tuple{transition}; }
6634};
6635
6637 : FixedArityOperationT<2, FindOrderedHashEntryOp> {
6638 enum class Kind : uint8_t {
6639 kFindOrderedHashMapEntry,
6640 kFindOrderedHashMapEntryForInt32Key,
6641 kFindOrderedHashSetEntry,
6642 };
6644
6645 static constexpr OpEffects effects =
6648 switch (kind) {
6649 case Kind::kFindOrderedHashMapEntry:
6650 case Kind::kFindOrderedHashSetEntry:
6651 return RepVector<RegisterRepresentation::Tagged()>();
6652 case Kind::kFindOrderedHashMapEntryForInt32Key:
6653 return RepVector<RegisterRepresentation::WordPtr()>();
6654 }
6655 }
6656
6659 return kind == Kind::kFindOrderedHashMapEntryForInt32Key
6660 ? MaybeRepVector<MaybeRegisterRepresentation::Tagged(),
6661 MaybeRegisterRepresentation::Word32()>()
6662 : MaybeRepVector<MaybeRegisterRepresentation::Tagged(),
6663 MaybeRegisterRepresentation::Tagged()>();
6664 }
6665
6666 OpIndex data_structure() const { return Base::input(0); }
6667 V<Word32> key() const { return Base::input<Word32>(1); }
6668
6670 : Base(data_structure, key), kind(kind) {}
6671
6672
6673 auto options() const { return std::tuple{kind}; }
6674};
6675V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
6677
6678struct CommentOp : FixedArityOperationT<0, CommentOp> {
6679 const char* message;
6680
6681 // Comments should not be removed.
6682 static constexpr OpEffects effects = OpEffects().RequiredWhenUnused();
6683
6684 explicit CommentOp(const char* message) : message(message) {}
6685
6687
6692
6693 auto options() const { return std::tuple{message}; }
6694};
6695
6696#if V8_ENABLE_WEBASSEMBLY
6697
6698V8_EXPORT_PRIVATE const RegisterRepresentation& RepresentationFor(
6699 wasm::ValueType type);
6700
6701struct GlobalGetOp : FixedArityOperationT<1, GlobalGetOp> {
6702 const wasm::WasmGlobal* global;
6703 static constexpr OpEffects effects = OpEffects().CanReadMemory();
6704
6705 V<WasmTrustedInstanceData> instance() const {
6706 return input<WasmTrustedInstanceData>(0);
6707 }
6708
6709 GlobalGetOp(V<WasmTrustedInstanceData> instance,
6710 const wasm::WasmGlobal* global)
6711 : Base(instance), global(global) {}
6712
6713 base::Vector<const RegisterRepresentation> outputs_rep() const {
6714 const RegisterRepresentation& repr = RepresentationFor(global->type);
6715 return base::VectorOf(&repr, 1);
6716 }
6717
6718 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
6719 ZoneVector<MaybeRegisterRepresentation>& storage) const {
6720 return MaybeRepVector<MaybeRegisterRepresentation::Tagged()>();
6721 }
6722
6723
6724 auto options() const { return std::tuple{global}; }
6725};
6726
6727struct GlobalSetOp : FixedArityOperationT<2, GlobalSetOp> {
6728 const wasm::WasmGlobal* global;
6729 static constexpr OpEffects effects = OpEffects().CanWriteMemory();
6730
6731 V<WasmTrustedInstanceData> instance() const {
6732 return input<WasmTrustedInstanceData>(0);
6733 }
6734 V<Any> value() const { return input<Any>(1); }
6735
6736 explicit GlobalSetOp(V<WasmTrustedInstanceData> instance, V<Any> value,
6737 const wasm::WasmGlobal* global)
6738 : Base(instance, value), global(global) {}
6739
6740 base::Vector<const RegisterRepresentation> outputs_rep() const { return {}; }
6741
6742 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
6743 ZoneVector<MaybeRegisterRepresentation>& storage) const {
6744 storage.resize(2);
6745 storage[0] = MaybeRegisterRepresentation::Tagged();
6746 storage[1] = MaybeRegisterRepresentation(RepresentationFor(global->type));
6747 return base::VectorOf(storage);
6748 }
6749
6750 void Validate(const Graph& graph) const { DCHECK(global->mutability); }
6751
6752 auto options() const { return std::tuple{global}; }
6753};
6754
6755struct RootConstantOp : FixedArityOperationT<0, RootConstantOp> {
6757 static constexpr OpEffects effects = OpEffects();
6758
6759 explicit RootConstantOp(RootIndex index) : Base(), index(index) {}
6760
6761 base::Vector<const RegisterRepresentation> outputs_rep() const {
6762 return RepVector<RegisterRepresentation::Tagged()>();
6763 }
6764
6765 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
6766 ZoneVector<MaybeRegisterRepresentation>& storage) const {
6767 return {};
6768 }
6769
6770 void Validate(const Graph& graph) const {}
6771
6772 auto options() const { return std::tuple{index}; }
6773};
6774
6775struct IsRootConstantOp : FixedArityOperationT<1, IsRootConstantOp> {
6777 static constexpr OpEffects effects = OpEffects();
6778
6779 V<Object> object() const { return input<Object>(0); }
6780
6781 IsRootConstantOp(V<Object> object, RootIndex index)
6782 : Base(object), index(index) {}
6783
6784 base::Vector<const RegisterRepresentation> outputs_rep() const {
6785 return RepVector<RegisterRepresentation::Word32()>();
6786 }
6787
6788 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
6789 ZoneVector<MaybeRegisterRepresentation>& storage) const {
6790 return MaybeRepVector<MaybeRegisterRepresentation::Tagged()>();
6791 }
6792
6793 auto options() const { return std::tuple{index}; }
6794};
6795
6796struct NullOp : FixedArityOperationT<0, NullOp> {
6797 wasm::ValueType type;
6798 static constexpr OpEffects effects = OpEffects();
6799
6800 explicit NullOp(wasm::ValueType type) : Base(), type(type) {}
6801
6802 base::Vector<const RegisterRepresentation> outputs_rep() const {
6803 return RepVector<RegisterRepresentation::Tagged()>();
6804 }
6805
6806 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
6807 ZoneVector<MaybeRegisterRepresentation>& storage) const {
6808 return {};
6809 }
6810
6811 void Validate(const Graph& graph) const {
6812 DCHECK(type.is_object_reference() && type.is_nullable());
6813 }
6814
6815 auto options() const { return std::tuple{type}; }
6816};
6817
6818struct IsNullOp : FixedArityOperationT<1, IsNullOp> {
6819 wasm::ValueType type;
6820 static constexpr OpEffects effects = OpEffects();
6821
6822 V<Object> object() const { return input<Object>(0); }
6823
6824 IsNullOp(V<Object> object, wasm::ValueType type) : Base(object), type(type) {}
6825
6826 base::Vector<const RegisterRepresentation> outputs_rep() const {
6827 return RepVector<RegisterRepresentation::Word32()>();
6828 }
6829
6830 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
6831 ZoneVector<MaybeRegisterRepresentation>& storage) const {
6832 return MaybeRepVector<MaybeRegisterRepresentation::Tagged()>();
6833 }
6834
6835 auto options() const { return std::tuple{type}; }
6836};
6837
6838// Traps on a null input, otherwise returns the input, type-cast to the
6839// respective non-nullable type.
6840struct AssertNotNullOp : FixedArityOperationT<1, AssertNotNullOp> {
6841 wasm::ValueType type;
6842 TrapId trap_id;
6843
6844 // Lowers to a trap and inherits {TrapIf}'s effects.
6845 static constexpr OpEffects effects =
6846 OpEffects().CanDependOnChecks().CanLeaveCurrentFunction();
6847
6848 V<Object> object() const { return input<Object>(0); }
6849
6850 AssertNotNullOp(V<Object> object, wasm::ValueType type, TrapId trap_id)
6851 : Base(object), type(type), trap_id(trap_id) {}
6852
6853 base::Vector<const RegisterRepresentation> outputs_rep() const {
6854 return RepVector<RegisterRepresentation::Tagged()>();
6855 }
6856
6857 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
6858 ZoneVector<MaybeRegisterRepresentation>& storage) const {
6859 return MaybeRepVector<MaybeRegisterRepresentation::Tagged()>();
6860 }
6861
6862 void Validate(const Graph& graph) const {
6863 // TODO(14108): Validate.
6864 }
6865
6866 auto options() const { return std::tuple{type, trap_id}; }
6867};
6868
6869// The runtime type (RTT) is a value representing a concrete type (in this case
6870// heap-type). The canonical RTTs are implicitly created values and invisible to
6871// the user in wasm-gc MVP. (See
6872// https://github.com/WebAssembly/gc/blob/main/proposals/gc/MVP.md#runtime-types)
6873struct RttCanonOp : FixedArityOperationT<1, RttCanonOp> {
6874 wasm::ModuleTypeIndex type_index;
6875
6876 static constexpr OpEffects effects = OpEffects();
6877
6878 explicit RttCanonOp(V<FixedArray> rtts, wasm::ModuleTypeIndex type_index)
6879 : Base(rtts), type_index(type_index) {}
6880
6881 V<FixedArray> rtts() const { return input<FixedArray>(0); }
6882
6883 base::Vector<const RegisterRepresentation> outputs_rep() const {
6884 return RepVector<RegisterRepresentation::Tagged()>();
6885 }
6886
6887 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
6888 ZoneVector<MaybeRegisterRepresentation>& storage) const {
6889 return MaybeRepVector<MaybeRegisterRepresentation::Tagged()>();
6890 }
6891
6892 auto options() const { return std::tuple{type_index}; }
6893};
6894
6895struct WasmTypeCheckOp : OperationT<WasmTypeCheckOp> {
6896 WasmTypeCheckConfig config;
6897
6898 static constexpr OpEffects effects = OpEffects().AssumesConsistentHeap();
6899
6900 WasmTypeCheckOp(V<Object> object, OptionalV<Map> rtt,
6901 WasmTypeCheckConfig config)
6902 : Base(1 + rtt.valid()), config(config) {
6903 input(0) = object;
6904 if (rtt.valid()) {
6905 input(1) = rtt.value();
6906 }
6907 }
6908
6909 template <typename Fn, typename Mapper>
6910 V8_INLINE auto Explode(Fn fn, Mapper& mapper) const {
6911 return fn(mapper.Map(object()), mapper.Map(rtt()), config);
6912 }
6913
6914 V<Object> object() const { return Base::input<Object>(0); }
6915 OptionalV<Map> rtt() const {
6916 return input_count > 1 ? input<Map>(1) : OptionalV<Map>::Nullopt();
6917 }
6918
6919 base::Vector<const RegisterRepresentation> outputs_rep() const {
6920 return RepVector<RegisterRepresentation::Word32()>();
6921 }
6922
6923 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
6924 ZoneVector<MaybeRegisterRepresentation>& storage) const {
6925 return input_count > 1
6926 ? MaybeRepVector<MaybeRegisterRepresentation::Tagged(),
6927 MaybeRegisterRepresentation::Tagged()>()
6928 : MaybeRepVector<MaybeRegisterRepresentation::Tagged()>();
6929 }
6930
6931
6932 auto options() const { return std::tuple{config}; }
6933
6934 static WasmTypeCheckOp& New(Graph* graph, V<Object> object,
6935 OptionalV<Map> rtt, WasmTypeCheckConfig config) {
6936 return Base::New(graph, 1 + rtt.valid(), object, rtt, config);
6937 }
6938};
6939
6940struct WasmTypeCastOp : OperationT<WasmTypeCastOp> {
6941 WasmTypeCheckConfig config;
6942
6943 static constexpr OpEffects effects = OpEffects().CanLeaveCurrentFunction();
6944
6945 WasmTypeCastOp(V<Object> object, OptionalV<Map> rtt,
6946 WasmTypeCheckConfig config)
6947 : Base(1 + rtt.valid()), config(config) {
6948 input(0) = object;
6949 if (rtt.valid()) {
6950 input(1) = rtt.value();
6951 }
6952 }
6953
6954 template <typename Fn, typename Mapper>
6955 V8_INLINE auto Explode(Fn fn, Mapper& mapper) const {
6956 return fn(mapper.Map(object()), mapper.Map(rtt()), config);
6957 }
6958
6959 V<Object> object() const { return Base::input<Object>(0); }
6960 OptionalV<Map> rtt() const {
6961 return input_count > 1 ? input<Map>(1) : OptionalV<Map>::Nullopt();
6962 }
6963
6964 base::Vector<const RegisterRepresentation> outputs_rep() const {
6965 return RepVector<RegisterRepresentation::Tagged()>();
6966 }
6967
6968 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
6969 ZoneVector<MaybeRegisterRepresentation>& storage) const {
6970 return input_count > 1
6971 ? MaybeRepVector<MaybeRegisterRepresentation::Tagged(),
6972 MaybeRegisterRepresentation::Tagged()>()
6973 : MaybeRepVector<MaybeRegisterRepresentation::Tagged()>();
6974 }
6975
6976
6977 auto options() const { return std::tuple{config}; }
6978
6979 static WasmTypeCastOp& New(Graph* graph, V<Object> object, OptionalV<Map> rtt,
6980 WasmTypeCheckConfig config) {
6981 return Base::New(graph, 1 + rtt.valid(), object, rtt, config);
6982 }
6983};
6984
6985// Annotate a value with a wasm type.
6986// This is a helper operation to propagate type information from the graph
6987// builder to type-based optimizations and will then be removed.
6988struct WasmTypeAnnotationOp : FixedArityOperationT<1, WasmTypeAnnotationOp> {
6989 static constexpr OpEffects effects = OpEffects();
6990 wasm::ValueType type;
6991
6992 explicit WasmTypeAnnotationOp(V<Object> value, wasm::ValueType type)
6993 : Base(value), type(type) {}
6994
6995 V<Object> value() const { return Base::input<Object>(0); }
6996
6997 base::Vector<const RegisterRepresentation> outputs_rep() const {
6998 return RepVector<RegisterRepresentation::Tagged()>();
6999 }
7000
7001 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
7002 ZoneVector<MaybeRegisterRepresentation>& storage) const {
7003 return MaybeRepVector<MaybeRegisterRepresentation::Tagged()>();
7004 }
7005
7006 void Validate(const Graph& graph) const {
7007 // In theory, the operation could be used for non-reference types as well.
7008 // This would require updating inputs_rep and outputs_rep to be based on
7009 // the wasm type.
7010 DCHECK(type.is_object_reference());
7011 }
7012
7013 auto options() const { return std::tuple(type); }
7014};
7015
7016struct AnyConvertExternOp : FixedArityOperationT<1, AnyConvertExternOp> {
7017 static constexpr OpEffects effects =
7018 SmiValuesAre31Bits() ? OpEffects().CanReadMemory()
7019 : OpEffects().CanReadMemory().CanAllocate();
7020
7021 explicit AnyConvertExternOp(V<Object> object) : Base(object) {}
7022
7023 V<Object> object() const { return Base::input<Object>(0); }
7024
7025 base::Vector<const RegisterRepresentation> outputs_rep() const {
7026 return RepVector<RegisterRepresentation::Tagged()>();
7027 }
7028
7029 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
7030 ZoneVector<MaybeRegisterRepresentation>& storage) const {
7031 return MaybeRepVector<MaybeRegisterRepresentation::Tagged()>();
7032 }
7033
7034
7035 auto options() const { return std::tuple(); }
7036};
7037
7038struct ExternConvertAnyOp : FixedArityOperationT<1, ExternConvertAnyOp> {
7039 static constexpr OpEffects effects = OpEffects();
7040
7041 explicit ExternConvertAnyOp(V<Object> object) : Base(object) {}
7042
7043 V<Object> object() const { return Base::input<Object>(0); }
7044
7045 base::Vector<const RegisterRepresentation> outputs_rep() const {
7046 return RepVector<RegisterRepresentation::Tagged()>();
7047 }
7048
7049 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
7050 ZoneVector<MaybeRegisterRepresentation>& storage) const {
7051 return MaybeRepVector<MaybeRegisterRepresentation::Tagged()>();
7052 }
7053
7054
7055 auto options() const { return std::tuple(); }
7056};
7057
7058struct StructGetOp : FixedArityOperationT<1, StructGetOp> {
7059 bool is_signed; // `false` only for unsigned packed type accesses.
7060 CheckForNull null_check;
7061 const wasm::StructType* type;
7062 wasm::ModuleTypeIndex type_index;
7063 int field_index;
7064
7065 OpEffects Effects() const {
7066 OpEffects result =
7067 OpEffects()
7068 // This should not float above a protective null check.
7069 .CanDependOnChecks()
7070 .CanReadMemory();
7071 if (null_check == kWithNullCheck) {
7072 // This may trap.
7073 result = result.CanLeaveCurrentFunction();
7074 }
7075 return result;
7076 }
7077
7078 StructGetOp(V<WasmStructNullable> object, const wasm::StructType* type,
7079 wasm::ModuleTypeIndex type_index, int field_index, bool is_signed,
7080 CheckForNull null_check)
7081 : Base(object),
7083 null_check(null_check),
7084 type(type),
7085 type_index(type_index),
7086 field_index(field_index) {}
7087
7088 V<WasmStructNullable> object() const { return input<WasmStructNullable>(0); }
7089
7090 base::Vector<const RegisterRepresentation> outputs_rep() const {
7091 return base::VectorOf(&RepresentationFor(type->field(field_index)), 1);
7092 }
7093
7094 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
7095 ZoneVector<MaybeRegisterRepresentation>& storage) const {
7096 return MaybeRepVector<MaybeRegisterRepresentation::Tagged()>();
7097 }
7098
7099 void Validate(const Graph& graph) const {
7100 DCHECK_LT(field_index, type->field_count());
7101 DCHECK_IMPLIES(!is_signed, type->field(field_index).is_packed());
7102 }
7103
7104 auto options() const {
7105 return std::tuple{type, type_index, field_index, is_signed, null_check};
7106 }
7107};
7108
7109struct StructSetOp : FixedArityOperationT<2, StructSetOp> {
7110 CheckForNull null_check;
7111 const wasm::StructType* type;
7112 // TODO(jkummerow): If we stored the ValueType here, that would save a few
7113 // lookups later.
7114 wasm::ModuleTypeIndex type_index;
7115 int field_index;
7116
7117 OpEffects Effects() const {
7118 OpEffects result =
7119 OpEffects()
7120 // This should not float above a protective null check.
7121 .CanDependOnChecks()
7122 .CanWriteMemory();
7123 if (null_check == kWithNullCheck) {
7124 // This may trap.
7125 result = result.CanLeaveCurrentFunction();
7126 }
7127 return result;
7128 }
7129
7130 StructSetOp(V<WasmStructNullable> object, V<Any> value,
7131 const wasm::StructType* type, wasm::ModuleTypeIndex type_index,
7132 int field_index, CheckForNull null_check)
7133 : Base(object, value),
7134 null_check(null_check),
7135 type(type),
7136 type_index(type_index),
7137 field_index(field_index) {}
7138
7139 V<WasmStructNullable> object() const { return input<WasmStructNullable>(0); }
7140 V<Any> value() const { return input(1); }
7141
7142 base::Vector<const RegisterRepresentation> outputs_rep() const { return {}; }
7143
7144 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
7145 ZoneVector<MaybeRegisterRepresentation>& storage) const {
7146 storage.resize(2);
7147 storage[0] = RegisterRepresentation::Tagged();
7148 storage[1] = RepresentationFor(type->field(field_index));
7149 return base::VectorOf(storage);
7150 }
7151
7152 void Validate(const Graph& graph) const {
7153 DCHECK_LT(field_index, type->field_count());
7154 }
7155
7156 auto options() const {
7157 return std::tuple{type, type_index, field_index, null_check};
7158 }
7159};
7160
7161struct ArrayGetOp : FixedArityOperationT<2, ArrayGetOp> {
7162 bool is_signed;
7163 const wasm::ArrayType* array_type;
7164
7165 // ArrayGetOp may never trap as it is always protected by a length check.
7166 static constexpr OpEffects effects =
7167 OpEffects()
7168 // This should not float above a protective null/length check.
7169 .CanDependOnChecks()
7170 .CanReadMemory();
7171
7172 ArrayGetOp(V<WasmArrayNullable> array, V<Word32> index,
7173 const wasm::ArrayType* array_type, bool is_signed)
7174 : Base(array, index), is_signed(is_signed), array_type(array_type) {}
7175
7176 V<WasmArrayNullable> array() const { return input<WasmArrayNullable>(0); }
7177 V<Word32> index() const { return input<Word32>(1); }
7178
7179 base::Vector<const RegisterRepresentation> outputs_rep() const {
7180 return base::VectorOf(&RepresentationFor(array_type->element_type()), 1);
7181 }
7182
7183 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
7184 ZoneVector<MaybeRegisterRepresentation>& storage) const {
7185 return MaybeRepVector<MaybeRegisterRepresentation::Tagged(),
7186 MaybeRegisterRepresentation::Word32()>();
7187 }
7188
7189
7190 auto options() const { return std::tuple{array_type, is_signed}; }
7191 void PrintOptions(std::ostream& os) const;
7192};
7193
7194struct ArraySetOp : FixedArityOperationT<3, ArraySetOp> {
7195 wasm::ValueType element_type;
7196
7197 // ArraySetOp may never trap as it is always protected by a length check.
7198 static constexpr OpEffects effects =
7199 OpEffects()
7200 // This should not float above a protective null/length check.
7201 .CanDependOnChecks()
7202 .CanWriteMemory();
7203
7204 ArraySetOp(V<WasmArrayNullable> array, V<Word32> index, V<Any> value,
7205 wasm::ValueType element_type)
7206 : Base(array, index, value), element_type(element_type) {}
7207
7208 V<WasmArrayNullable> array() const { return input<WasmArrayNullable>(0); }
7209 V<Word32> index() const { return input<Word32>(1); }
7210 V<Any> value() const { return input<Any>(2); }
7211
7212 base::Vector<const RegisterRepresentation> outputs_rep() const { return {}; }
7213
7214 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
7215 ZoneVector<MaybeRegisterRepresentation>& storage) const {
7216 return InitVectorOf(storage, {RegisterRepresentation::Tagged(),
7217 RegisterRepresentation::Word32(),
7218 RepresentationFor(element_type)});
7219 }
7220
7221
7222 auto options() const { return std::tuple{element_type}; }
7223};
7224
7225struct ArrayLengthOp : FixedArityOperationT<1, ArrayLengthOp> {
7226 CheckForNull null_check;
7227
7228 OpEffects Effects() const {
7229 OpEffects result =
7230 OpEffects()
7231 // This should not float above a protective null check.
7232 .CanDependOnChecks()
7233 .CanReadMemory();
7234 if (null_check == kWithNullCheck) {
7235 // This may trap.
7236 result = result.CanLeaveCurrentFunction();
7237 }
7238 return result;
7239 }
7240
7241 explicit ArrayLengthOp(V<WasmArrayNullable> array, CheckForNull null_check)
7242 : Base(array), null_check(null_check) {}
7243
7244 V<WasmArrayNullable> array() const { return input<WasmArrayNullable>(0); }
7245
7246 base::Vector<const RegisterRepresentation> outputs_rep() const {
7247 return RepVector<RegisterRepresentation::Word32()>();
7248 }
7249
7250 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
7251 ZoneVector<MaybeRegisterRepresentation>& storage) const {
7252 return MaybeRepVector<RegisterRepresentation::Tagged()>();
7253 }
7254
7255
7256 auto options() const { return std::tuple{null_check}; }
7257};
7258
7259struct WasmAllocateArrayOp : FixedArityOperationT<2, WasmAllocateArrayOp> {
7260 static constexpr OpEffects effects =
7261 OpEffects().CanAllocate().CanLeaveCurrentFunction();
7262
7263 const wasm::ArrayType* array_type;
7264
7265 explicit WasmAllocateArrayOp(V<Map> rtt, V<Word32> length,
7266 const wasm::ArrayType* array_type)
7267 : Base(rtt, length), array_type(array_type) {}
7268
7269 V<Map> rtt() const { return Base::input<Map>(0); }
7270 V<Word32> length() const { return Base::input<Word32>(1); }
7271
7272 base::Vector<const RegisterRepresentation> outputs_rep() const {
7273 return RepVector<RegisterRepresentation::Tagged()>();
7274 }
7275
7276 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
7277 ZoneVector<MaybeRegisterRepresentation>& storage) const {
7278 return MaybeRepVector<MaybeRegisterRepresentation::Tagged(),
7279 MaybeRegisterRepresentation::Word32()>();
7280 }
7281
7282 auto options() const { return std::tuple{array_type}; }
7283 void PrintOptions(std::ostream& os) const;
7284};
7285
7286struct WasmAllocateStructOp : FixedArityOperationT<1, WasmAllocateStructOp> {
7287 static constexpr OpEffects effects =
7288 OpEffects().CanAllocate().CanLeaveCurrentFunction();
7289
7290 const wasm::StructType* struct_type;
7291
7292 explicit WasmAllocateStructOp(V<Map> rtt, const wasm::StructType* struct_type)
7293 : Base(rtt), struct_type(struct_type) {}
7294
7295 V<Map> rtt() const { return Base::input<Map>(0); }
7296
7297 base::Vector<const RegisterRepresentation> outputs_rep() const {
7298 return RepVector<RegisterRepresentation::Tagged()>();
7299 }
7300
7301 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
7302 ZoneVector<MaybeRegisterRepresentation>& storage) const {
7303 return MaybeRepVector<MaybeRegisterRepresentation::Tagged()>();
7304 }
7305
7306 auto options() const { return std::tuple{struct_type}; }
7307};
7308
7309struct WasmRefFuncOp : FixedArityOperationT<1, WasmRefFuncOp> {
7310 static constexpr OpEffects effects = OpEffects().CanAllocate();
7311 uint32_t function_index;
7312
7313 explicit WasmRefFuncOp(V<WasmTrustedInstanceData> wasm_instance,
7314 uint32_t function_index)
7315 : Base(wasm_instance), function_index(function_index) {}
7316
7317 V<WasmTrustedInstanceData> instance() const {
7318 return input<WasmTrustedInstanceData>(0);
7319 }
7320
7321 base::Vector<const RegisterRepresentation> outputs_rep() const {
7322 return RepVector<RegisterRepresentation::Tagged()>();
7323 }
7324
7325 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
7326 ZoneVector<MaybeRegisterRepresentation>& storage) const {
7327 return MaybeRepVector<MaybeRegisterRepresentation::Tagged()>();
7328 }
7329
7330 auto options() const { return std::tuple{function_index}; }
7331};
7332
7333// Casts a JavaScript string to a flattened wtf16 string.
7334// TODO(14108): Can we optimize stringref operations without adding this as a
7335// special operations?
7336struct StringAsWtf16Op : FixedArityOperationT<1, StringAsWtf16Op> {
7337 static constexpr OpEffects effects =
7338 OpEffects()
7339 // This should not float above a protective null/length check.
7340 .CanDependOnChecks()
7341 .CanReadMemory();
7342
7343 explicit StringAsWtf16Op(V<String> string) : Base(string) {}
7344
7345 V<String> string() const { return input<String>(0); }
7346
7347 base::Vector<const RegisterRepresentation> outputs_rep() const {
7348 return RepVector<RegisterRepresentation::Tagged()>();
7349 }
7350
7351 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
7352 ZoneVector<MaybeRegisterRepresentation>& storage) const {
7353 return MaybeRepVector<RegisterRepresentation::Tagged()>();
7354 }
7355
7356 auto options() const { return std::tuple{}; }
7357};
7358
7359// Takes a flattened string and extracts the first string pointer, the base
7360// offset and the character width shift.
7361struct StringPrepareForGetCodeUnitOp
7362 : FixedArityOperationT<1, StringPrepareForGetCodeUnitOp> {
7363 static constexpr OpEffects effects =
7364 OpEffects()
7365 // This should not float above a protective null/length check.
7366 .CanDependOnChecks();
7367
7368 explicit StringPrepareForGetCodeUnitOp(V<Object> string) : Base(string) {}
7369
7370 OpIndex string() const { return input(0); }
7371
7372 base::Vector<const RegisterRepresentation> outputs_rep() const {
7373 return RepVector<RegisterRepresentation::Tagged(),
7374 RegisterRepresentation::WordPtr(),
7375 RegisterRepresentation::Word32()>();
7376 }
7377
7378 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
7379 ZoneVector<MaybeRegisterRepresentation>& storage) const {
7380 return MaybeRepVector<RegisterRepresentation::Tagged()>();
7381 }
7382
7383 auto options() const { return std::tuple{}; }
7384};
7385
7386struct Simd128ConstantOp : FixedArityOperationT<0, Simd128ConstantOp> {
7387 static constexpr uint8_t kZero[kSimd128Size] = {};
7388 uint8_t value[kSimd128Size];
7389
7390 static constexpr OpEffects effects = OpEffects();
7391
7392 explicit Simd128ConstantOp(const uint8_t incoming_value[kSimd128Size])
7393 : Base() {
7394 std::copy(incoming_value, incoming_value + kSimd128Size, value);
7395 }
7396
7397 base::Vector<const RegisterRepresentation> outputs_rep() const {
7398 return RepVector<RegisterRepresentation::Simd128()>();
7399 }
7400
7401 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
7402 ZoneVector<MaybeRegisterRepresentation>& storage) const {
7403 return {};
7404 }
7405
7406 void Validate(const Graph& graph) const {
7407 // TODO(14108): Validate.
7408 }
7409
7410 bool IsZero() const { return std::memcmp(kZero, value, kSimd128Size) == 0; }
7411
7412 auto options() const { return std::tuple{value}; }
7413 void PrintOptions(std::ostream& os) const;
7414};
7415
7416#define FOREACH_SIMD_128_BINARY_SIGN_EXTENSION_OPCODE(V) \
7417 V(I16x8ExtMulLowI8x16S) \
7418 V(I16x8ExtMulHighI8x16S) \
7419 V(I16x8ExtMulLowI8x16U) \
7420 V(I16x8ExtMulHighI8x16U) \
7421 V(I32x4ExtMulLowI16x8S) \
7422 V(I32x4ExtMulHighI16x8S) \
7423 V(I32x4ExtMulLowI16x8U) \
7424 V(I32x4ExtMulHighI16x8U) \
7425 V(I64x2ExtMulLowI32x4S) \
7426 V(I64x2ExtMulHighI32x4S) \
7427 V(I64x2ExtMulLowI32x4U) \
7428 V(I64x2ExtMulHighI32x4U)
7429
7430#define FOREACH_SIMD_128_BINARY_BASIC_OPCODE(V) \
7431 V(I8x16Eq) \
7432 V(I8x16Ne) \
7433 V(I8x16GtS) \
7434 V(I8x16GtU) \
7435 V(I8x16GeS) \
7436 V(I8x16GeU) \
7437 V(I16x8Eq) \
7438 V(I16x8Ne) \
7439 V(I16x8GtS) \
7440 V(I16x8GtU) \
7441 V(I16x8GeS) \
7442 V(I16x8GeU) \
7443 V(I32x4Eq) \
7444 V(I32x4Ne) \
7445 V(I32x4GtS) \
7446 V(I32x4GtU) \
7447 V(I32x4GeS) \
7448 V(I32x4GeU) \
7449 V(F32x4Eq) \
7450 V(F32x4Ne) \
7451 V(F32x4Lt) \
7452 V(F32x4Le) \
7453 V(F64x2Eq) \
7454 V(F64x2Ne) \
7455 V(F64x2Lt) \
7456 V(F64x2Le) \
7457 V(S128And) \
7458 V(S128AndNot) \
7459 V(S128Or) \
7460 V(S128Xor) \
7461 V(I8x16SConvertI16x8) \
7462 V(I8x16UConvertI16x8) \
7463 V(I8x16Add) \
7464 V(I8x16AddSatS) \
7465 V(I8x16AddSatU) \
7466 V(I8x16Sub) \
7467 V(I8x16SubSatS) \
7468 V(I8x16SubSatU) \
7469 V(I8x16MinS) \
7470 V(I8x16MinU) \
7471 V(I8x16MaxS) \
7472 V(I8x16MaxU) \
7473 V(I8x16RoundingAverageU) \
7474 V(I16x8Q15MulRSatS) \
7475 V(I16x8SConvertI32x4) \
7476 V(I16x8UConvertI32x4) \
7477 V(I16x8Add) \
7478 V(I16x8AddSatS) \
7479 V(I16x8AddSatU) \
7480 V(I16x8Sub) \
7481 V(I16x8SubSatS) \
7482 V(I16x8SubSatU) \
7483 V(I16x8Mul) \
7484 V(I16x8MinS) \
7485 V(I16x8MinU) \
7486 V(I16x8MaxS) \
7487 V(I16x8MaxU) \
7488 V(I16x8RoundingAverageU) \
7489 V(I32x4Add) \
7490 V(I32x4Sub) \
7491 V(I32x4Mul) \
7492 V(I32x4MinS) \
7493 V(I32x4MinU) \
7494 V(I32x4MaxS) \
7495 V(I32x4MaxU) \
7496 V(I32x4DotI16x8S) \
7497 V(I64x2Add) \
7498 V(I64x2Sub) \
7499 V(I64x2Mul) \
7500 V(I64x2Eq) \
7501 V(I64x2Ne) \
7502 V(I64x2GtS) \
7503 V(I64x2GeS) \
7504 V(F32x4Add) \
7505 V(F32x4Sub) \
7506 V(F32x4Mul) \
7507 V(F32x4Div) \
7508 V(F32x4Min) \
7509 V(F32x4Max) \
7510 V(F32x4Pmin) \
7511 V(F32x4Pmax) \
7512 V(F64x2Add) \
7513 V(F64x2Sub) \
7514 V(F64x2Mul) \
7515 V(F64x2Div) \
7516 V(F64x2Min) \
7517 V(F64x2Max) \
7518 V(F64x2Pmin) \
7519 V(F64x2Pmax) \
7520 V(F32x4RelaxedMin) \
7521 V(F32x4RelaxedMax) \
7522 V(F64x2RelaxedMin) \
7523 V(F64x2RelaxedMax) \
7524 V(I16x8RelaxedQ15MulRS) \
7525 V(I16x8DotI8x16I7x16S) \
7526 FOREACH_SIMD_128_BINARY_SIGN_EXTENSION_OPCODE(V)
7527
7528#define FOREACH_SIMD_128_BINARY_SPECIAL_OPCODE(V) \
7529 V(I8x16Swizzle) \
7530 V(I8x16RelaxedSwizzle)
7531
7532#define FOREACH_SIMD_128_BINARY_MANDATORY_OPCODE(V) \
7533 FOREACH_SIMD_128_BINARY_BASIC_OPCODE(V) \
7534 FOREACH_SIMD_128_BINARY_SPECIAL_OPCODE(V)
7535
7536#define FOREACH_SIMD_128_BINARY_OPTIONAL_OPCODE(V) \
7537 V(F16x8Add) \
7538 V(F16x8Sub) \
7539 V(F16x8Mul) \
7540 V(F16x8Div) \
7541 V(F16x8Min) \
7542 V(F16x8Max) \
7543 V(F16x8Pmin) \
7544 V(F16x8Pmax) \
7545 V(F16x8Eq) \
7546 V(F16x8Ne) \
7547 V(F16x8Lt) \
7548 V(F16x8Le)
7549
7550#define FOREACH_SIMD_128_BINARY_OPCODE(V) \
7551 FOREACH_SIMD_128_BINARY_MANDATORY_OPCODE(V) \
7552 FOREACH_SIMD_128_BINARY_OPTIONAL_OPCODE(V)
7553
7554struct Simd128BinopOp : FixedArityOperationT<2, Simd128BinopOp> {
7555 // clang-format off
7556 enum class Kind : uint8_t {
7557#define DEFINE_KIND(kind) k##kind,
7558 FOREACH_SIMD_128_BINARY_OPCODE(DEFINE_KIND)
7559 kFirstSignExtensionOp = kI16x8ExtMulLowI8x16S,
7560 kLastSignExtensionOp = kI64x2ExtMulHighI32x4U,
7561#undef DEFINE_KIND
7562 };
7563 // clang-format on
7564
7565 Kind kind;
7566
7567 static bool IsCommutative(Kind kind) {
7568 switch (kind) {
7569 // TODO(14108): Explicitly list all commutative SIMD operations.
7570 case Kind::kI64x2Add:
7571 case Kind::kI32x4Add:
7572 case Kind::kI16x8Add:
7573 case Kind::kI8x16Add:
7574 case Kind::kF64x2Add:
7575 case Kind::kF32x4Add:
7576
7577 case Kind::kI64x2Mul:
7578 case Kind::kI32x4Mul:
7579 case Kind::kI16x8Mul:
7580 case Kind::kF64x2Mul:
7581 case Kind::kF32x4Mul:
7582
7583 case Kind::kS128Xor:
7584 return true;
7585 default:
7586 return false;
7587 }
7588 }
7589
7590 static constexpr OpEffects effects = OpEffects();
7591
7592 base::Vector<const RegisterRepresentation> outputs_rep() const {
7593 return RepVector<RegisterRepresentation::Simd128()>();
7594 }
7595
7596 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
7597 ZoneVector<MaybeRegisterRepresentation>& storage) const {
7598 return MaybeRepVector<RegisterRepresentation::Simd128(),
7599 RegisterRepresentation::Simd128()>();
7600 }
7601
7602 Simd128BinopOp(V<Simd128> left, V<Simd128> right, Kind kind)
7603 : Base(left, right), kind(kind) {}
7604
7605 V<Simd128> left() const { return input<Simd128>(0); }
7606 V<Simd128> right() const { return input<Simd128>(1); }
7607
7608
7609 auto options() const { return std::tuple{kind}; }
7610};
7611V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
7612 Simd128BinopOp::Kind kind);
7613
7614#define FOREACH_SIMD_128_UNARY_SIGN_EXTENSION_OPCODE(V) \
7615 V(I16x8SConvertI8x16Low) \
7616 V(I16x8SConvertI8x16High) \
7617 V(I16x8UConvertI8x16Low) \
7618 V(I16x8UConvertI8x16High) \
7619 V(I32x4SConvertI16x8Low) \
7620 V(I32x4SConvertI16x8High) \
7621 V(I32x4UConvertI16x8Low) \
7622 V(I32x4UConvertI16x8High) \
7623 V(I64x2SConvertI32x4Low) \
7624 V(I64x2SConvertI32x4High) \
7625 V(I64x2UConvertI32x4Low) \
7626 V(I64x2UConvertI32x4High)
7627
7628#define FOREACH_SIMD_128_UNARY_NON_OPTIONAL_OPCODE(V) \
7629 V(S128Not) \
7630 V(F32x4DemoteF64x2Zero) \
7631 V(F64x2PromoteLowF32x4) \
7632 V(I8x16Abs) \
7633 V(I8x16Neg) \
7634 V(I8x16Popcnt) \
7635 V(I16x8ExtAddPairwiseI8x16S) \
7636 V(I16x8ExtAddPairwiseI8x16U) \
7637 V(I32x4ExtAddPairwiseI16x8S) \
7638 V(I32x4ExtAddPairwiseI16x8U) \
7639 V(I16x8Abs) \
7640 V(I16x8Neg) \
7641 V(I32x4Abs) \
7642 V(I32x4Neg) \
7643 V(I64x2Abs) \
7644 V(I64x2Neg) \
7645 V(F32x4Abs) \
7646 V(F32x4Neg) \
7647 V(F32x4Sqrt) \
7648 V(F64x2Abs) \
7649 V(F64x2Neg) \
7650 V(F64x2Sqrt) \
7651 V(I32x4SConvertF32x4) \
7652 V(I32x4UConvertF32x4) \
7653 V(F32x4SConvertI32x4) \
7654 V(F32x4UConvertI32x4) \
7655 V(I32x4TruncSatF64x2SZero) \
7656 V(I32x4TruncSatF64x2UZero) \
7657 V(F64x2ConvertLowI32x4S) \
7658 V(F64x2ConvertLowI32x4U) \
7659 V(I32x4RelaxedTruncF32x4S) \
7660 V(I32x4RelaxedTruncF32x4U) \
7661 V(I32x4RelaxedTruncF64x2SZero) \
7662 V(I32x4RelaxedTruncF64x2UZero) \
7663 FOREACH_SIMD_128_UNARY_SIGN_EXTENSION_OPCODE(V)
7664
7665#define FOREACH_SIMD_128_UNARY_OPTIONAL_OPCODE(V) \
7666 V(F16x8Abs) \
7667 V(F16x8Neg) \
7668 V(F16x8Sqrt) \
7669 V(F16x8Ceil) \
7670 V(F16x8Floor) \
7671 V(F16x8Trunc) \
7672 V(F16x8NearestInt) \
7673 V(I16x8SConvertF16x8) \
7674 V(I16x8UConvertF16x8) \
7675 V(F16x8SConvertI16x8) \
7676 V(F16x8UConvertI16x8) \
7677 V(F16x8DemoteF32x4Zero) \
7678 V(F16x8DemoteF64x2Zero) \
7679 V(F32x4PromoteLowF16x8) \
7680 V(F32x4Ceil) \
7681 V(F32x4Floor) \
7682 V(F32x4Trunc) \
7683 V(F32x4NearestInt) \
7684 V(F64x2Ceil) \
7685 V(F64x2Floor) \
7686 V(F64x2Trunc) \
7687 V(F64x2NearestInt) \
7688 /* TODO(mliedtke): Rename to ReverseBytes once the naming is decoupled from \
7689 * Turbofan naming. */ \
7690 V(Simd128ReverseBytes)
7691
7692#define FOREACH_SIMD_128_UNARY_OPCODE(V) \
7693 FOREACH_SIMD_128_UNARY_NON_OPTIONAL_OPCODE(V) \
7694 FOREACH_SIMD_128_UNARY_OPTIONAL_OPCODE(V)
7695
7696struct Simd128UnaryOp : FixedArityOperationT<1, Simd128UnaryOp> {
7697 // clang-format off
7698 enum class Kind : uint8_t {
7699#define DEFINE_KIND(kind) k##kind,
7700 FOREACH_SIMD_128_UNARY_OPCODE(DEFINE_KIND)
7701 kFirstSignExtensionOp = kI16x8SConvertI8x16Low,
7702 kLastSignExtensionOp = kI64x2UConvertI32x4High,
7703#undef DEFINE_KIND
7704 };
7705 // clang-format on
7706
7707 Kind kind;
7708
7709 static constexpr OpEffects effects = OpEffects();
7710
7711 base::Vector<const RegisterRepresentation> outputs_rep() const {
7712 return RepVector<RegisterRepresentation::Simd128()>();
7713 }
7714
7715 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
7716 ZoneVector<MaybeRegisterRepresentation>& storage) const {
7717 return MaybeRepVector<RegisterRepresentation::Simd128()>();
7718 }
7719
7720 Simd128UnaryOp(V<Simd128> input, Kind kind) : Base(input), kind(kind) {}
7721
7722 V<Simd128> input() const { return Base::input<Simd128>(0); }
7723
7724
7725 auto options() const { return std::tuple{kind}; }
7726};
7727V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
7728 Simd128UnaryOp::Kind kind);
7729
7730#define FOREACH_SIMD_128_REDUCE_OPTIONAL_OPCODE(V) \
7731 V(I8x16AddReduce) \
7732 V(I16x8AddReduce) \
7733 V(I32x4AddReduce) \
7734 V(I64x2AddReduce) \
7735 V(F32x4AddReduce) \
7736 V(F64x2AddReduce)
7737
7738struct Simd128ReduceOp : FixedArityOperationT<1, Simd128ReduceOp> {
7739 enum class Kind : uint8_t {
7740#define DEFINE_KIND(kind) k##kind,
7741 FOREACH_SIMD_128_REDUCE_OPTIONAL_OPCODE(DEFINE_KIND)
7742#undef DEFINE_KIND
7743 };
7744
7745 Kind kind;
7746
7747 static constexpr OpEffects effects = OpEffects();
7748
7749 base::Vector<const RegisterRepresentation> outputs_rep() const {
7750 return RepVector<RegisterRepresentation::Simd128()>();
7751 }
7752
7753 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
7754 ZoneVector<MaybeRegisterRepresentation>& storage) const {
7755 return MaybeRepVector<RegisterRepresentation::Simd128()>();
7756 }
7757
7758 Simd128ReduceOp(V<Simd128> input, Kind kind) : Base(input), kind(kind) {}
7759
7760 V<Simd128> input() const { return Base::input<Simd128>(0); }
7761
7762
7763 auto options() const { return std::tuple{kind}; }
7764};
7765V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
7766 Simd128ReduceOp::Kind kind);
7767
7768#define FOREACH_SIMD_128_SHIFT_OPCODE(V) \
7769 V(I8x16Shl) \
7770 V(I8x16ShrS) \
7771 V(I8x16ShrU) \
7772 V(I16x8Shl) \
7773 V(I16x8ShrS) \
7774 V(I16x8ShrU) \
7775 V(I32x4Shl) \
7776 V(I32x4ShrS) \
7777 V(I32x4ShrU) \
7778 V(I64x2Shl) \
7779 V(I64x2ShrS) \
7780 V(I64x2ShrU)
7781
7782struct Simd128ShiftOp : FixedArityOperationT<2, Simd128ShiftOp> {
7783 enum class Kind : uint8_t {
7784#define DEFINE_KIND(kind) k##kind,
7785 FOREACH_SIMD_128_SHIFT_OPCODE(DEFINE_KIND)
7786#undef DEFINE_KIND
7787 };
7788
7789 Kind kind;
7790
7791 static constexpr OpEffects effects = OpEffects();
7792
7793 base::Vector<const RegisterRepresentation> outputs_rep() const {
7794 return RepVector<RegisterRepresentation::Simd128()>();
7795 }
7796
7797 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
7798 ZoneVector<MaybeRegisterRepresentation>& storage) const {
7799 return MaybeRepVector<RegisterRepresentation::Simd128(),
7800 RegisterRepresentation::Word32()>();
7801 }
7802
7803 Simd128ShiftOp(V<Simd128> input, V<Word32> shift, Kind kind)
7804 : Base(input, shift), kind(kind) {}
7805
7806 V<Simd128> input() const { return Base::input<Simd128>(0); }
7807 V<Word32> shift() const { return Base::input<Word32>(1); }
7808
7809
7810 auto options() const { return std::tuple{kind}; }
7811};
7812V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
7813 Simd128ShiftOp::Kind kind);
7814
7815#define FOREACH_SIMD_128_TEST_OPCODE(V) \
7816 V(V128AnyTrue) \
7817 V(I8x16AllTrue) \
7818 V(I8x16BitMask) \
7819 V(I16x8AllTrue) \
7820 V(I16x8BitMask) \
7821 V(I32x4AllTrue) \
7822 V(I32x4BitMask) \
7823 V(I64x2AllTrue) \
7824 V(I64x2BitMask)
7825
7826struct Simd128TestOp : FixedArityOperationT<1, Simd128TestOp> {
7827 enum class Kind : uint8_t {
7828#define DEFINE_KIND(kind) k##kind,
7829 FOREACH_SIMD_128_TEST_OPCODE(DEFINE_KIND)
7830#undef DEFINE_KIND
7831 };
7832
7833 Kind kind;
7834
7835 static constexpr OpEffects effects = OpEffects();
7836
7837 base::Vector<const RegisterRepresentation> outputs_rep() const {
7838 return RepVector<RegisterRepresentation::Word32()>();
7839 }
7840
7841 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
7842 ZoneVector<MaybeRegisterRepresentation>& storage) const {
7843 return MaybeRepVector<RegisterRepresentation::Simd128()>();
7844 }
7845
7846 Simd128TestOp(V<Simd128> input, Kind kind) : Base(input), kind(kind) {}
7847
7848 V<Simd128> input() const { return Base::input<Simd128>(0); }
7849
7850
7851 auto options() const { return std::tuple{kind}; }
7852};
7853V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
7854 Simd128TestOp::Kind kind);
7855
7856#define FOREACH_SIMD_128_SPLAT_MANDATORY_OPCODE(V) \
7857 V(I8x16) \
7858 V(I16x8) \
7859 V(I32x4) \
7860 V(I64x2) \
7861 V(F32x4) \
7862 V(F64x2)
7863
7864#define FOREACH_SIMD_128_SPLAT_OPCODE(V) \
7865 FOREACH_SIMD_128_SPLAT_MANDATORY_OPCODE(V) \
7866 V(F16x8)
7867struct Simd128SplatOp : FixedArityOperationT<1, Simd128SplatOp> {
7868 enum class Kind : uint8_t {
7869#define DEFINE_KIND(kind) k##kind,
7870 FOREACH_SIMD_128_SPLAT_OPCODE(DEFINE_KIND)
7871#undef DEFINE_KIND
7872 };
7873
7874 Kind kind;
7875
7876 static constexpr OpEffects effects = OpEffects();
7877
7878 base::Vector<const RegisterRepresentation> outputs_rep() const {
7879 return RepVector<RegisterRepresentation::Simd128()>();
7880 }
7881
7882 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
7883 ZoneVector<MaybeRegisterRepresentation>& storage) const {
7884 switch (kind) {
7885 case Kind::kI8x16:
7886 case Kind::kI16x8:
7887 case Kind::kI32x4:
7888 return MaybeRepVector<RegisterRepresentation::Word32()>();
7889 case Kind::kI64x2:
7890 return MaybeRepVector<RegisterRepresentation::Word64()>();
7891 case Kind::kF16x8:
7892 case Kind::kF32x4:
7893 return MaybeRepVector<RegisterRepresentation::Float32()>();
7894 case Kind::kF64x2:
7895 return MaybeRepVector<RegisterRepresentation::Float64()>();
7896 }
7897 }
7898
7899 Simd128SplatOp(V<Any> input, Kind kind) : Base(input), kind(kind) {}
7900
7901 V<Any> input() const { return Base::input<Any>(0); }
7902
7903
7904 auto options() const { return std::tuple{kind}; }
7905};
7906V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
7907 Simd128SplatOp::Kind kind);
7908
7909#define FOREACH_SIMD_128_TERNARY_MASK_OPCODE(V) \
7910 V(S128Select) \
7911 V(I8x16RelaxedLaneSelect) \
7912 V(I16x8RelaxedLaneSelect) \
7913 V(I32x4RelaxedLaneSelect) \
7914 V(I64x2RelaxedLaneSelect)
7915
7916#define FOREACH_SIMD_128_TERNARY_OTHER_OPCODE(V) \
7917 V(F32x4Qfma) \
7918 V(F32x4Qfms) \
7919 V(F64x2Qfma) \
7920 V(F64x2Qfms) \
7921 V(I32x4DotI8x16I7x16AddS)
7922
7923#define FOREACH_SIMD_128_TERNARY_OPTIONAL_OPCODE(V) \
7924 V(F16x8Qfma) \
7925 V(F16x8Qfms)
7926
7927#define FOREACH_SIMD_128_TERNARY_OPCODE(V) \
7928 FOREACH_SIMD_128_TERNARY_MASK_OPCODE(V) \
7929 FOREACH_SIMD_128_TERNARY_OTHER_OPCODE(V) \
7930 FOREACH_SIMD_128_TERNARY_OPTIONAL_OPCODE(V)
7931
7932struct Simd128TernaryOp : FixedArityOperationT<3, Simd128TernaryOp> {
7933 enum class Kind : uint8_t {
7934#define DEFINE_KIND(kind) k##kind,
7935 FOREACH_SIMD_128_TERNARY_OPCODE(DEFINE_KIND)
7936#undef DEFINE_KIND
7937 };
7938
7939 Kind kind;
7940
7941 static constexpr OpEffects effects = OpEffects();
7942
7943 base::Vector<const RegisterRepresentation> outputs_rep() const {
7944 return RepVector<RegisterRepresentation::Simd128()>();
7945 }
7946
7947 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
7948 ZoneVector<MaybeRegisterRepresentation>& storage) const {
7949 return MaybeRepVector<RegisterRepresentation::Simd128(),
7950 RegisterRepresentation::Simd128(),
7951 RegisterRepresentation::Simd128()>();
7952 }
7953
7954 Simd128TernaryOp(V<Simd128> first, V<Simd128> second, V<Simd128> third,
7955 Kind kind)
7956 : Base(first, second, third), kind(kind) {}
7957
7958 V<Simd128> first() const { return input<Simd128>(0); }
7959 V<Simd128> second() const { return input<Simd128>(1); }
7960 V<Simd128> third() const { return input<Simd128>(2); }
7961
7962
7963 auto options() const { return std::tuple{kind}; }
7964};
7965V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
7966 Simd128TernaryOp::Kind kind);
7967
7968struct Simd128ExtractLaneOp : FixedArityOperationT<1, Simd128ExtractLaneOp> {
7969 enum class Kind : uint8_t {
7970 kI8x16S,
7971 kI8x16U,
7972 kI16x8S,
7973 kI16x8U,
7974 kI32x4,
7975 kI64x2,
7976 kF16x8,
7977 kF32x4,
7978 kF64x2,
7979 };
7980
7981 Kind kind;
7982 uint8_t lane;
7983
7984 static constexpr OpEffects effects = OpEffects();
7985
7986 static MachineRepresentation element_rep(Kind kind) {
7987 switch (kind) {
7988 case Kind::kI8x16S:
7989 case Kind::kI8x16U:
7990 return MachineRepresentation::kWord8;
7991 case Kind::kI16x8S:
7992 case Kind::kI16x8U:
7993 return MachineRepresentation::kWord16;
7994 case Kind::kI32x4:
7995 return MachineRepresentation::kWord32;
7996 case Kind::kI64x2:
7997 return MachineRepresentation::kWord64;
7998 case Kind::kF16x8:
7999 case Kind::kF32x4:
8000 return MachineRepresentation::kFloat32;
8001 case Kind::kF64x2:
8002 return MachineRepresentation::kFloat64;
8003 }
8004 }
8005
8006 base::Vector<const RegisterRepresentation> outputs_rep() const {
8007 switch (kind) {
8008 case Kind::kI8x16S:
8009 case Kind::kI8x16U:
8010 case Kind::kI16x8S:
8011 case Kind::kI16x8U:
8012 case Kind::kI32x4:
8013 return RepVector<RegisterRepresentation::Word32()>();
8014 case Kind::kI64x2:
8015 return RepVector<RegisterRepresentation::Word64()>();
8016 case Kind::kF16x8:
8017 case Kind::kF32x4:
8018 return RepVector<RegisterRepresentation::Float32()>();
8019 case Kind::kF64x2:
8020 return RepVector<RegisterRepresentation::Float64()>();
8021 }
8022 }
8023
8024 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
8025 ZoneVector<MaybeRegisterRepresentation>& storage) const {
8026 return MaybeRepVector<RegisterRepresentation::Simd128()>();
8027 }
8028
8029 Simd128ExtractLaneOp(V<Simd128> input, Kind kind, uint8_t lane)
8030 : Base(input), kind(kind), lane(lane) {}
8031
8032 V<Simd128> input() const { return Base::input<Simd128>(0); }
8033
8034 void Validate(const Graph& graph) const {
8035#if DEBUG
8036 uint8_t lane_count;
8037 switch (kind) {
8038 case Kind::kI8x16S:
8039 case Kind::kI8x16U:
8040 lane_count = 16;
8041 break;
8042 case Kind::kI16x8S:
8043 case Kind::kI16x8U:
8044 case Kind::kF16x8:
8045 lane_count = 8;
8046 break;
8047 case Kind::kI32x4:
8048 case Kind::kF32x4:
8049 lane_count = 4;
8050 break;
8051 case Kind::kI64x2:
8052 case Kind::kF64x2:
8053 lane_count = 2;
8054 break;
8055 }
8056 DCHECK_LT(lane, lane_count);
8057#endif
8058 }
8059
8060 auto options() const { return std::tuple{kind, lane}; }
8061 void PrintOptions(std::ostream& os) const;
8062};
8063
8064struct Simd128ReplaceLaneOp : FixedArityOperationT<2, Simd128ReplaceLaneOp> {
8065 enum class Kind : uint8_t {
8066 kI8x16,
8067 kI16x8,
8068 kI32x4,
8069 kI64x2,
8070 kF16x8,
8071 kF32x4,
8072 kF64x2,
8073 };
8074
8075 Kind kind;
8076 uint8_t lane;
8077
8078 static constexpr OpEffects effects = OpEffects();
8079
8080 base::Vector<const RegisterRepresentation> outputs_rep() const {
8081 return RepVector<RegisterRepresentation::Simd128()>();
8082 }
8083 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
8084 ZoneVector<MaybeRegisterRepresentation>& storage) const {
8085 return InitVectorOf(storage,
8086 {RegisterRepresentation::Simd128(), new_lane_rep()});
8087 }
8088
8089 Simd128ReplaceLaneOp(V<Simd128> into, V<Any> new_lane, Kind kind,
8090 uint8_t lane)
8091 : Base(into, new_lane), kind(kind), lane(lane) {}
8092
8093 V<Simd128> into() const { return input<Simd128>(0); }
8094 V<Any> new_lane() const { return input<Any>(1); }
8095
8096 void Validate(const Graph& graph) const {
8097#if DEBUG
8098 uint8_t lane_count;
8099 switch (kind) {
8100 case Kind::kI8x16:
8101 lane_count = 16;
8102 break;
8103 case Kind::kI16x8:
8104 case Kind::kF16x8:
8105 lane_count = 8;
8106 break;
8107 case Kind::kI32x4:
8108 case Kind::kF32x4:
8109 lane_count = 4;
8110 break;
8111 case Kind::kI64x2:
8112 case Kind::kF64x2:
8113 lane_count = 2;
8114 break;
8115 }
8116 DCHECK_LT(lane, lane_count);
8117#endif
8118 }
8119
8120 auto options() const { return std::tuple{kind, lane}; }
8121 void PrintOptions(std::ostream& os) const;
8122
8123 RegisterRepresentation new_lane_rep() const {
8124 switch (kind) {
8125 case Kind::kI8x16:
8126 case Kind::kI16x8:
8127 case Kind::kI32x4:
8128 return RegisterRepresentation::Word32();
8129 case Kind::kI64x2:
8130 return RegisterRepresentation::Word64();
8131 case Kind::kF16x8:
8132 case Kind::kF32x4:
8133 return RegisterRepresentation::Float32();
8134 case Kind::kF64x2:
8135 return RegisterRepresentation::Float64();
8136 }
8137 }
8138};
8139
8140// If `mode` is `kLoad`, load a value from `base() + index() + offset`, whose
8141// size is determinded by `lane_kind`, and return the Simd128 `value()` with
8142// the lane specified by `lane_kind` and `lane` replaced with the loaded value.
8143// If `mode` is `kStore`, extract the lane specified by `lane` with size
8144// `lane_kind` from `value()`, and store it to `base() + index() + offset`.
8145struct Simd128LaneMemoryOp : FixedArityOperationT<3, Simd128LaneMemoryOp> {
8146 enum class Mode : bool { kLoad, kStore };
8147 using Kind = LoadOp::Kind;
8148 // The values encode the element_size_log2.
8149 enum class LaneKind : uint8_t { k8 = 0, k16 = 1, k32 = 2, k64 = 3 };
8150
8151 Mode mode;
8152 Kind kind;
8153 LaneKind lane_kind;
8154 uint8_t lane;
8155 int offset;
8156
8157 OpEffects Effects() const {
8158 OpEffects effects = mode == Mode::kLoad ? OpEffects().CanReadMemory()
8159 : OpEffects().CanWriteMemory();
8160 effects = effects.CanDependOnChecks();
8161 if (kind.with_trap_handler) effects = effects.CanLeaveCurrentFunction();
8162 return effects;
8163 }
8164
8165 base::Vector<const RegisterRepresentation> outputs_rep() const {
8166 return mode == Mode::kLoad ? RepVector<RegisterRepresentation::Simd128()>()
8167 : RepVector<>();
8168 }
8169
8170 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
8171 ZoneVector<MaybeRegisterRepresentation>& storage) const {
8172 return MaybeRepVector<RegisterRepresentation::WordPtr(),
8173 RegisterRepresentation::WordPtr(),
8174 RegisterRepresentation::Simd128()>();
8175 }
8176
8177 Simd128LaneMemoryOp(OpIndex base, OpIndex index, OpIndex value, Mode mode,
8178 Kind kind, LaneKind lane_kind, uint8_t lane, int offset)
8179 : Base(base, index, value),
8180 mode(mode),
8181 kind(kind),
8182 lane_kind(lane_kind),
8183 lane(lane),
8184 offset(offset) {}
8185
8186 OpIndex base() const { return input(0); }
8187 OpIndex index() const { return input(1); }
8188 OpIndex value() const { return input(2); }
8189 uint8_t lane_size() const { return 1 << static_cast<uint8_t>(lane_kind); }
8190
8191 void Validate(const Graph& graph) {
8192 DCHECK(!kind.tagged_base);
8193#if DEBUG
8194 uint8_t lane_count;
8195 switch (lane_kind) {
8196 case LaneKind::k8:
8197 lane_count = 16;
8198 break;
8199 case LaneKind::k16:
8200 lane_count = 8;
8201 break;
8202 case LaneKind::k32:
8203 lane_count = 4;
8204 break;
8205 case LaneKind::k64:
8206 lane_count = 2;
8207 break;
8208 }
8209 DCHECK_LT(lane, lane_count);
8210#endif
8211 }
8212
8213 auto options() const {
8214 return std::tuple{mode, kind, lane_kind, lane, offset};
8215 }
8216 void PrintOptions(std::ostream& os) const;
8217};
8218
8219#define FOREACH_SIMD_128_LOAD_TRANSFORM_OPCODE(V) \
8220 V(8x8S) \
8221 V(8x8U) \
8222 V(16x4S) \
8223 V(16x4U) \
8224 V(32x2S) \
8225 V(32x2U) \
8226 V(8Splat) \
8227 V(16Splat) \
8228 V(32Splat) \
8229 V(64Splat) \
8230 V(32Zero) \
8231 V(64Zero)
8232
8233// Load a value from `base() + index() + offset`, whose size is determinded by
8234// `transform_kind`, and generate a Simd128 value as follows:
8235// - From 8x8S to 32x2U (extend kinds), the loaded value has size 8. It is
8236// interpreted as a vector of values according to the size of the kind, which
8237// populate the even lanes of the generated value. The odd lanes are zero- or
8238// sign-extended according to the kind.
8239// - For splat kinds, the loaded value's size is determined by the kind, all
8240// lanes of the generated value are populated with the loaded value.
8241// - For "zero" kinds, the loaded value's size is determined by the kind, and
8242// the generated value zero-extends the loaded value.
8243struct Simd128LoadTransformOp
8244 : FixedArityOperationT<2, Simd128LoadTransformOp> {
8245 using LoadKind = LoadOp::Kind;
8246 enum class TransformKind : uint8_t {
8247#define DEFINE_KIND(kind) k##kind,
8248 FOREACH_SIMD_128_LOAD_TRANSFORM_OPCODE(DEFINE_KIND)
8249#undef DEFINE_KIND
8250 };
8251
8252 LoadKind load_kind;
8253 TransformKind transform_kind;
8254 int offset;
8255
8256 OpEffects Effects() const {
8257 OpEffects effects = OpEffects().CanReadMemory().CanDependOnChecks();
8258 if (load_kind.with_trap_handler) {
8259 effects = effects.CanLeaveCurrentFunction();
8260 }
8261 return effects;
8262 }
8263
8264 base::Vector<const RegisterRepresentation> outputs_rep() const {
8265 return RepVector<RegisterRepresentation::Simd128()>();
8266 }
8267
8268 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
8269 ZoneVector<MaybeRegisterRepresentation>& storage) const {
8270 return MaybeRepVector<RegisterRepresentation::WordPtr(),
8271 RegisterRepresentation::WordPtr()>();
8272 }
8273
8274 Simd128LoadTransformOp(V<WordPtr> base, V<WordPtr> index, LoadKind load_kind,
8275 TransformKind transform_kind, int offset)
8276 : Base(base, index),
8277 load_kind(load_kind),
8278 transform_kind(transform_kind),
8279 offset(offset) {}
8280
8281 V<WordPtr> base() const { return input<WordPtr>(0); }
8282 V<WordPtr> index() const { return input<WordPtr>(1); }
8283
8284 void Validate(const Graph& graph) { DCHECK(!load_kind.tagged_base); }
8285
8286 auto options() const { return std::tuple{load_kind, transform_kind, offset}; }
8287 void PrintOptions(std::ostream& os) const;
8288};
8289
8290// Takes two Simd128 inputs and generates a Simd128 value. The 8-bit lanes of
8291// both inputs are numbered 0-31, and each output 8-bit lane is selected from
8292// among the input lanes according to `shuffle`.
8293struct Simd128ShuffleOp : FixedArityOperationT<2, Simd128ShuffleOp> {
8294 enum class Kind : uint8_t {
8295 kI8x2,
8296 kI8x4,
8297 kI8x8,
8298 kI8x16,
8299 };
8300
8301 uint8_t shuffle[kSimd128Size] = {0};
8302 const Kind kind;
8303
8304 static constexpr OpEffects effects = OpEffects();
8305
8306 base::Vector<const RegisterRepresentation> outputs_rep() const {
8307 return RepVector<RegisterRepresentation::Simd128()>();
8308 }
8309
8310 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
8311 ZoneVector<MaybeRegisterRepresentation>& storage) const {
8312 return MaybeRepVector<RegisterRepresentation::Simd128(),
8313 RegisterRepresentation::Simd128()>();
8314 }
8315
8316 Simd128ShuffleOp(V<Simd128> left, V<Simd128> right, Kind kind,
8317 const uint8_t incoming_shuffle[kSimd128Size])
8318 : Base(left, right), kind(kind) {
8319 uint8_t count = 0;
8320 switch (kind) {
8321 default:
8322 UNREACHABLE();
8323 case Kind::kI8x2:
8324 count = 2;
8325 break;
8326 case Kind::kI8x4:
8327 count = 4;
8328 break;
8329 case Kind::kI8x8:
8330 count = 8;
8331 break;
8332 case Kind::kI8x16:
8333 count = 16;
8334 break;
8335 }
8336 std::copy_n(incoming_shuffle, count, shuffle);
8337 }
8338
8339 V<Simd128> left() const { return input<Simd128>(0); }
8340 V<Simd128> right() const { return input<Simd128>(1); }
8341
8342 void Validate(const Graph& graph) {
8343#if DEBUG
8344 constexpr uint8_t kNumberOfLanesForShuffle = 32;
8345 for (uint8_t index : shuffle) {
8346 DCHECK_LT(index, kNumberOfLanesForShuffle);
8347 }
8348#endif
8349 }
8350
8351 auto options() const { return std::tuple{kind, shuffle}; }
8352 void PrintOptions(std::ostream& os) const;
8353};
8354
8355#if V8_ENABLE_WASM_DEINTERLEAVED_MEM_OPS
8356
8357// Load from memory and write the result into two registers, the first
8358// containing the even numbered elements and the other containing the odd
8359// elements.
8360struct Simd128LoadPairDeinterleaveOp
8361 : FixedArityOperationT<2, Simd128LoadPairDeinterleaveOp> {
8362 using LoadKind = LoadOp::Kind;
8363 enum class Kind : uint8_t {
8364 k8x32,
8365 k16x16,
8366 k32x8,
8367 k64x4,
8368 };
8369
8370 LoadKind load_kind;
8371 Kind kind;
8372
8373 OpEffects Effects() const {
8374 OpEffects effects = OpEffects().CanReadMemory().CanDependOnChecks();
8375 if (load_kind.with_trap_handler) {
8376 effects = effects.CanLeaveCurrentFunction();
8377 }
8378 return effects;
8379 }
8380
8381 base::Vector<const RegisterRepresentation> outputs_rep() const {
8382 return RepVector<RegisterRepresentation::Simd128(),
8383 RegisterRepresentation::Simd128()>();
8384 }
8385
8386 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
8387 ZoneVector<MaybeRegisterRepresentation>& storage) const {
8388 return MaybeRepVector<RegisterRepresentation::WordPtr(),
8389 RegisterRepresentation::WordPtr()>();
8390 }
8391
8392 Simd128LoadPairDeinterleaveOp(V<WordPtr> base, V<WordPtr> index,
8393 LoadKind load_kind, Kind kind)
8394 : Base(base, index), load_kind(load_kind), kind(kind) {}
8395
8396 V<WordPtr> base() const { return Base::input<WordPtr>(0); }
8397 V<WordPtr> index() const { return Base::input<WordPtr>(1); }
8398
8399 uint32_t lane_size() const {
8400 switch (kind) {
8401 case Kind::k8x32:
8402 return 8;
8403 case Kind::k16x16:
8404 return 16;
8405 case Kind::k32x8:
8406 return 32;
8407 case Kind::k64x4:
8408 return 64;
8409 }
8410 }
8411
8412 void Validate(const Graph& graph) const { DCHECK(!load_kind.tagged_base); }
8413
8414 auto options() const { return std::tuple{load_kind, kind}; }
8415
8416 void PrintOptions(std::ostream& os) const;
8417};
8418
8419#endif // V8_ENABLE_WASM_DEINTERLEAVED_MEM_OPS
8420
8421#if V8_ENABLE_WASM_SIMD256_REVEC
8422
8423struct Simd256ConstantOp : FixedArityOperationT<0, Simd256ConstantOp> {
8424 static constexpr uint8_t kZero[kSimd256Size] = {};
8425 uint8_t value[kSimd256Size];
8426
8427 static constexpr OpEffects effects = OpEffects();
8428
8429 explicit Simd256ConstantOp(const uint8_t incoming_value[kSimd256Size])
8430 : Base() {
8431 std::copy(incoming_value, incoming_value + kSimd256Size, value);
8432 }
8433
8434 base::Vector<const RegisterRepresentation> outputs_rep() const {
8435 return RepVector<RegisterRepresentation::Simd256()>();
8436 }
8437
8438 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
8439 ZoneVector<MaybeRegisterRepresentation>& storage) const {
8440 return {};
8441 }
8442
8443 void Validate(const Graph& graph) const {
8444 // TODO(14108): Validate.
8445 }
8446
8447 bool IsZero() const { return std::memcmp(kZero, value, kSimd256Size) == 0; }
8448
8449 auto options() const { return std::tuple{value}; }
8450 void PrintOptions(std::ostream& os) const;
8451};
8452
8453struct Simd256Extract128LaneOp
8454 : FixedArityOperationT<1, Simd256Extract128LaneOp> {
8455 uint8_t lane;
8456
8457 static constexpr OpEffects effects = OpEffects();
8458
8459 base::Vector<const RegisterRepresentation> outputs_rep() const {
8460 return RepVector<RegisterRepresentation::Simd128()>();
8461 }
8462
8463 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
8464 ZoneVector<MaybeRegisterRepresentation>& storage) const {
8465 return MaybeRepVector<RegisterRepresentation::Simd256()>();
8466 }
8467
8468 Simd256Extract128LaneOp(OpIndex input, uint8_t lane)
8469 : Base(input), lane(lane) {}
8470
8471 OpIndex input() const { return Base::input(0); }
8472
8473 void Validate(const Graph& graph) const {
8474#if DEBUG
8475 DCHECK_LT(lane, 2);
8476#endif
8477 }
8478
8479 auto options() const { return std::tuple{lane}; }
8480 void PrintOptions(std::ostream& os) const;
8481};
8482
8483#define FOREACH_SIMD_256_LOAD_TRANSFORM_OPCODE(V) \
8484 V(8x16S) \
8485 V(8x16U) \
8486 V(8x8U) \
8487 V(16x8S) \
8488 V(16x8U) \
8489 V(32x4S) \
8490 V(32x4U) \
8491 V(8Splat) \
8492 V(16Splat) \
8493 V(32Splat) \
8494 V(64Splat)
8495
8496struct Simd256LoadTransformOp
8497 : FixedArityOperationT<2, Simd256LoadTransformOp> {
8498 using LoadKind = LoadOp::Kind;
8499 enum class TransformKind : uint8_t {
8500#define DEFINE_KIND(kind) k##kind,
8501 FOREACH_SIMD_256_LOAD_TRANSFORM_OPCODE(DEFINE_KIND)
8502#undef DEFINE_KIND
8503 };
8504
8505 LoadKind load_kind;
8506 TransformKind transform_kind;
8507 int offset;
8508
8509 OpEffects Effects() const {
8510 OpEffects effects = OpEffects().CanReadMemory().CanDependOnChecks();
8511 if (load_kind.with_trap_handler) {
8512 effects = effects.CanLeaveCurrentFunction();
8513 }
8514 return effects;
8515 }
8516
8517 base::Vector<const RegisterRepresentation> outputs_rep() const {
8518 return RepVector<RegisterRepresentation::Simd256()>();
8519 }
8520
8521 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
8522 ZoneVector<MaybeRegisterRepresentation>& storage) const {
8523 return MaybeRepVector<RegisterRepresentation::WordPtr(),
8524 RegisterRepresentation::WordPtr()>();
8525 }
8526
8527 Simd256LoadTransformOp(V<WordPtr> base, V<WordPtr> index, LoadKind load_kind,
8528 TransformKind transform_kind, int offset)
8529 : Base(base, index),
8530 load_kind(load_kind),
8531 transform_kind(transform_kind),
8532 offset(offset) {}
8533
8534 V<WordPtr> base() const { return input<WordPtr>(0); }
8535 V<WordPtr> index() const { return input<WordPtr>(1); }
8536
8537 void Validate(const Graph& graph) { DCHECK(!load_kind.tagged_base); }
8538
8539 auto options() const { return std::tuple{load_kind, transform_kind, offset}; }
8540 void PrintOptions(std::ostream& os) const;
8541};
8542
8543#define FOREACH_SIMD_256_UNARY_SIGN_EXTENSION_OPCODE(V) \
8544 V(I16x16SConvertI8x16) \
8545 V(I16x16UConvertI8x16) \
8546 V(I32x8SConvertI16x8) \
8547 V(I32x8UConvertI16x8) \
8548 V(I64x4SConvertI32x4) \
8549 V(I64x4UConvertI32x4)
8550
8551#define FOREACH_SIMD_256_UNARY_OPCODE(V) \
8552 V(S256Not) \
8553 V(I8x32Abs) \
8554 V(I8x32Neg) \
8555 V(I16x16ExtAddPairwiseI8x32S) \
8556 V(I16x16ExtAddPairwiseI8x32U) \
8557 V(I32x8ExtAddPairwiseI16x16S) \
8558 V(I32x8ExtAddPairwiseI16x16U) \
8559 V(I16x16Abs) \
8560 V(I16x16Neg) \
8561 V(I32x8Abs) \
8562 V(I32x8Neg) \
8563 V(F32x8Abs) \
8564 V(F32x8Neg) \
8565 V(F32x8Sqrt) \
8566 V(F64x4Abs) \
8567 V(F64x4Neg) \
8568 V(F64x4Sqrt) \
8569 V(I32x8UConvertF32x8) \
8570 V(I32x8SConvertF32x8) \
8571 V(F32x8UConvertI32x8) \
8572 V(F32x8SConvertI32x8) \
8573 V(I32x8RelaxedTruncF32x8S) \
8574 V(I32x8RelaxedTruncF32x8U) \
8575 FOREACH_SIMD_256_UNARY_SIGN_EXTENSION_OPCODE(V)
8576
8577struct Simd256UnaryOp : FixedArityOperationT<1, Simd256UnaryOp> {
8578 // clang-format off
8579 enum class Kind : uint8_t {
8580#define DEFINE_KIND(kind) k##kind,
8581 FOREACH_SIMD_256_UNARY_OPCODE(DEFINE_KIND)
8582 kFirstSignExtensionOp = kI16x16SConvertI8x16,
8583 kLastSignExtensionOp = kI64x4UConvertI32x4,
8584#undef DEFINE_KIND
8585 };
8586 // clang-format on
8587
8588 Kind kind;
8589
8590 static constexpr OpEffects effects = OpEffects();
8591
8592 base::Vector<const RegisterRepresentation> outputs_rep() const {
8593 return RepVector<RegisterRepresentation::Simd256()>();
8594 }
8595
8596 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
8597 ZoneVector<MaybeRegisterRepresentation>& storage) const {
8598 if (kind >= Kind::kFirstSignExtensionOp) {
8599 return MaybeRepVector<RegisterRepresentation::Simd128()>();
8600 } else {
8601 return MaybeRepVector<RegisterRepresentation::Simd256()>();
8602 }
8603 }
8604
8605 Simd256UnaryOp(OpIndex input, Kind kind) : Base(input), kind(kind) {}
8606
8607 OpIndex input() const { return Base::input(0); }
8608
8609
8610 auto options() const { return std::tuple{kind}; }
8611};
8612std::ostream& operator<<(std::ostream& os, Simd256UnaryOp::Kind kind);
8613
8614#define FOREACH_SIMD_256_BINARY_SIGN_EXTENSION_OPCODE(V) \
8615 V(I64x4ExtMulI32x4S) \
8616 V(I64x4ExtMulI32x4U) \
8617 V(I32x8ExtMulI16x8S) \
8618 V(I32x8ExtMulI16x8U) \
8619 V(I16x16ExtMulI8x16S) \
8620 V(I16x16ExtMulI8x16U)
8621
8622#define FOREACH_SIMD_256_BINARY_OPCODE(V) \
8623 V(I8x32Eq) \
8624 V(I8x32Ne) \
8625 V(I8x32GtS) \
8626 V(I8x32GtU) \
8627 V(I8x32GeS) \
8628 V(I8x32GeU) \
8629 V(I16x16Eq) \
8630 V(I16x16Ne) \
8631 V(I16x16GtS) \
8632 V(I16x16GtU) \
8633 V(I16x16GeS) \
8634 V(I16x16GeU) \
8635 V(I32x8Eq) \
8636 V(I32x8Ne) \
8637 V(I32x8GtS) \
8638 V(I32x8GtU) \
8639 V(I32x8GeS) \
8640 V(I32x8GeU) \
8641 V(F32x8Eq) \
8642 V(F32x8Ne) \
8643 V(F32x8Lt) \
8644 V(F32x8Le) \
8645 V(F64x4Eq) \
8646 V(F64x4Ne) \
8647 V(F64x4Lt) \
8648 V(F64x4Le) \
8649 V(S256And) \
8650 V(S256AndNot) \
8651 V(S256Or) \
8652 V(S256Xor) \
8653 V(I8x32SConvertI16x16) \
8654 V(I8x32UConvertI16x16) \
8655 V(I8x32Add) \
8656 V(I8x32AddSatS) \
8657 V(I8x32AddSatU) \
8658 V(I8x32Sub) \
8659 V(I8x32SubSatS) \
8660 V(I8x32SubSatU) \
8661 V(I8x32MinS) \
8662 V(I8x32MinU) \
8663 V(I8x32MaxS) \
8664 V(I8x32MaxU) \
8665 V(I8x32RoundingAverageU) \
8666 V(I16x16SConvertI32x8) \
8667 V(I16x16UConvertI32x8) \
8668 V(I16x16Add) \
8669 V(I16x16AddSatS) \
8670 V(I16x16AddSatU) \
8671 V(I16x16Sub) \
8672 V(I16x16SubSatS) \
8673 V(I16x16SubSatU) \
8674 V(I16x16Mul) \
8675 V(I16x16MinS) \
8676 V(I16x16MinU) \
8677 V(I16x16MaxS) \
8678 V(I16x16MaxU) \
8679 V(I16x16RoundingAverageU) \
8680 V(I32x8Add) \
8681 V(I32x8Sub) \
8682 V(I32x8Mul) \
8683 V(I32x8MinS) \
8684 V(I32x8MinU) \
8685 V(I32x8MaxS) \
8686 V(I32x8MaxU) \
8687 V(I32x8DotI16x16S) \
8688 V(I64x4Add) \
8689 V(I64x4Sub) \
8690 V(I64x4Mul) \
8691 V(I64x4Eq) \
8692 V(I64x4Ne) \
8693 V(I64x4GtS) \
8694 V(I64x4GeS) \
8695 V(F32x8Add) \
8696 V(F32x8Sub) \
8697 V(F32x8Mul) \
8698 V(F32x8Div) \
8699 V(F32x8Min) \
8700 V(F32x8Max) \
8701 V(F32x8Pmin) \
8702 V(F32x8Pmax) \
8703 V(F64x4Add) \
8704 V(F64x4Sub) \
8705 V(F64x4Mul) \
8706 V(F64x4Div) \
8707 V(F64x4Min) \
8708 V(F64x4Max) \
8709 V(F64x4Pmin) \
8710 V(F64x4Pmax) \
8711 V(F32x8RelaxedMin) \
8712 V(F32x8RelaxedMax) \
8713 V(F64x4RelaxedMin) \
8714 V(F64x4RelaxedMax) \
8715 V(I16x16DotI8x32I7x32S) \
8716 FOREACH_SIMD_256_BINARY_SIGN_EXTENSION_OPCODE(V)
8717
8718struct Simd256BinopOp : FixedArityOperationT<2, Simd256BinopOp> {
8719 // clang-format off
8720 enum class Kind : uint8_t {
8721#define DEFINE_KIND(kind) k##kind,
8722 FOREACH_SIMD_256_BINARY_OPCODE(DEFINE_KIND)
8723 kFirstSignExtensionOp = kI64x4ExtMulI32x4S,
8724 kLastSignExtensionOp = kI16x16ExtMulI8x16U,
8725#undef DEFINE_KIND
8726 };
8727 // clang-format on
8728
8729 Kind kind;
8730
8731 static constexpr OpEffects effects = OpEffects();
8732
8733 base::Vector<const RegisterRepresentation> outputs_rep() const {
8734 return RepVector<RegisterRepresentation::Simd256()>();
8735 }
8736
8737 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
8738 ZoneVector<MaybeRegisterRepresentation>& storage) const {
8739 if (kind >= Kind::kFirstSignExtensionOp) {
8740 return MaybeRepVector<RegisterRepresentation::Simd128(),
8741 RegisterRepresentation::Simd128()>();
8742 } else {
8743 return MaybeRepVector<RegisterRepresentation::Simd256(),
8744 RegisterRepresentation::Simd256()>();
8745 }
8746 }
8747
8748 Simd256BinopOp(OpIndex left, OpIndex right, Kind kind)
8749 : Base(left, right), kind(kind) {}
8750
8751 OpIndex left() const { return input(0); }
8752 OpIndex right() const { return input(1); }
8753
8754
8755 auto options() const { return std::tuple{kind}; }
8756};
8757
8758std::ostream& operator<<(std::ostream& os, Simd256BinopOp::Kind kind);
8759
8760#define FOREACH_SIMD_256_SHIFT_OPCODE(V) \
8761 V(I16x16Shl) \
8762 V(I16x16ShrS) \
8763 V(I16x16ShrU) \
8764 V(I32x8Shl) \
8765 V(I32x8ShrS) \
8766 V(I32x8ShrU) \
8767 V(I64x4Shl) \
8768 V(I64x4ShrU)
8769
8770struct Simd256ShiftOp : FixedArityOperationT<2, Simd256ShiftOp> {
8771 enum class Kind : uint8_t {
8772#define DEFINE_KIND(kind) k##kind,
8773 FOREACH_SIMD_256_SHIFT_OPCODE(DEFINE_KIND)
8774#undef DEFINE_KIND
8775 };
8776
8777 Kind kind;
8778
8779 static constexpr OpEffects effects = OpEffects();
8780
8781 base::Vector<const RegisterRepresentation> outputs_rep() const {
8782 return RepVector<RegisterRepresentation::Simd256()>();
8783 }
8784
8785 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
8786 ZoneVector<MaybeRegisterRepresentation>& storage) const {
8787 return MaybeRepVector<RegisterRepresentation::Simd256(),
8788 RegisterRepresentation::Word32()>();
8789 }
8790
8791 Simd256ShiftOp(V<Simd256> input, V<Word32> shift, Kind kind)
8792 : Base(input, shift), kind(kind) {}
8793
8794 V<Simd256> input() const { return Base::input<Simd256>(0); }
8795 V<Word32> shift() const { return Base::input<Word32>(1); }
8796
8797
8798 auto options() const { return std::tuple{kind}; }
8799};
8800V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
8801 Simd256ShiftOp::Kind kind);
8802
8803#define FOREACH_SIMD_256_TERNARY_MASK_OPCODE(V) \
8804 V(S256Select) \
8805 V(I8x32RelaxedLaneSelect) \
8806 V(I16x16RelaxedLaneSelect) \
8807 V(I32x8RelaxedLaneSelect) \
8808 V(I64x4RelaxedLaneSelect)
8809
8810#define FOREACH_SIMD_256_TERNARY_OTHER_OPCODE(V) \
8811 V(F32x8Qfma) \
8812 V(F32x8Qfms) \
8813 V(F64x4Qfma) \
8814 V(F64x4Qfms) \
8815 V(I32x8DotI8x32I7x32AddS)
8816
8817#define FOREACH_SIMD_256_TERNARY_OPCODE(V) \
8818 FOREACH_SIMD_256_TERNARY_MASK_OPCODE(V) \
8819 FOREACH_SIMD_256_TERNARY_OTHER_OPCODE(V)
8820
8821struct Simd256TernaryOp : FixedArityOperationT<3, Simd256TernaryOp> {
8822 enum class Kind : uint8_t {
8823#define DEFINE_KIND(kind) k##kind,
8824 FOREACH_SIMD_256_TERNARY_OPCODE(DEFINE_KIND)
8825#undef DEFINE_KIND
8826 };
8827
8828 Kind kind;
8829
8830 static constexpr OpEffects effects = OpEffects();
8831
8832 base::Vector<const RegisterRepresentation> outputs_rep() const {
8833 return RepVector<RegisterRepresentation::Simd256()>();
8834 }
8835
8836 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
8837 ZoneVector<MaybeRegisterRepresentation>& storage) const {
8838 return MaybeRepVector<RegisterRepresentation::Simd256(),
8839 RegisterRepresentation::Simd256(),
8840 RegisterRepresentation::Simd256()>();
8841 }
8842
8843 Simd256TernaryOp(V<Simd256> first, V<Simd256> second, V<Simd256> third,
8844 Kind kind)
8845 : Base(first, second, third), kind(kind) {}
8846
8847 V<Simd256> first() const { return input<Simd256>(0); }
8848 V<Simd256> second() const { return input<Simd256>(1); }
8849 V<Simd256> third() const { return input<Simd256>(2); }
8850
8851
8852 auto options() const { return std::tuple{kind}; }
8853};
8854std::ostream& operator<<(std::ostream& os, Simd256TernaryOp::Kind kind);
8855
8856#define FOREACH_SIMD_256_SPLAT_OPCODE(V) \
8857 V(I8x32) \
8858 V(I16x16) \
8859 V(I32x8) \
8860 V(I64x4) \
8861 V(F32x8) \
8862 V(F64x4)
8863
8864struct Simd256SplatOp : FixedArityOperationT<1, Simd256SplatOp> {
8865 enum class Kind : uint8_t {
8866#define DEFINE_KIND(kind) k##kind,
8867 FOREACH_SIMD_256_SPLAT_OPCODE(DEFINE_KIND)
8868#undef DEFINE_KIND
8869 };
8870
8871 Kind kind;
8872
8873 static constexpr OpEffects effects = OpEffects();
8874
8875 base::Vector<const RegisterRepresentation> outputs_rep() const {
8876 return RepVector<RegisterRepresentation::Simd256()>();
8877 }
8878
8879 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
8880 ZoneVector<MaybeRegisterRepresentation>& storage) const {
8881 switch (kind) {
8882 case Kind::kI8x32:
8883 case Kind::kI16x16:
8884 case Kind::kI32x8:
8885 return MaybeRepVector<RegisterRepresentation::Word32()>();
8886 case Kind::kI64x4:
8887 return MaybeRepVector<RegisterRepresentation::Word64()>();
8888 case Kind::kF32x8:
8889 return MaybeRepVector<RegisterRepresentation::Float32()>();
8890 case Kind::kF64x4:
8891 return MaybeRepVector<RegisterRepresentation::Float64()>();
8892 }
8893 }
8894
8895 Simd256SplatOp(OpIndex input, Kind kind) : Base(input), kind(kind) {}
8896
8897 OpIndex input() const { return Base::input(0); }
8898
8899
8900 auto options() const { return std::tuple{kind}; }
8901};
8902std::ostream& operator<<(std::ostream& os, Simd256SplatOp::Kind kind);
8903
8904struct SimdPack128To256Op : FixedArityOperationT<2, SimdPack128To256Op> {
8905 static constexpr OpEffects effects = OpEffects();
8906
8907 base::Vector<const RegisterRepresentation> outputs_rep() const {
8908 return RepVector<RegisterRepresentation::Simd256()>();
8909 }
8910
8911 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
8912 ZoneVector<MaybeRegisterRepresentation>& storage) const {
8913 return MaybeRepVector<RegisterRepresentation::Simd128(),
8914 RegisterRepresentation::Simd128()>();
8915 }
8916
8917 SimdPack128To256Op(V<Simd128> left, V<Simd128> right) : Base(left, right) {}
8918 V<Simd128> left() const { return Base::input<Simd128>(0); }
8919 V<Simd128> right() const { return Base::input<Simd128>(1); }
8920 auto options() const { return std::tuple{}; }
8921};
8922
8923#ifdef V8_TARGET_ARCH_X64
8924struct Simd256ShufdOp : FixedArityOperationT<1, Simd256ShufdOp> {
8925 static constexpr OpEffects effects = OpEffects();
8926 uint8_t control;
8927
8928 base::Vector<const RegisterRepresentation> outputs_rep() const {
8929 return RepVector<RegisterRepresentation::Simd256()>();
8930 }
8931
8932 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
8933 ZoneVector<MaybeRegisterRepresentation>& storage) const {
8934 return MaybeRepVector<RegisterRepresentation::Simd256()>();
8935 }
8936
8937 Simd256ShufdOp(V<Simd256> input, uint8_t control)
8938 : Base(input), control(control) {}
8939
8940 V<Simd256> input() const { return Base::input<Simd256>(0); }
8941
8942
8943 auto options() const { return std::tuple{control}; }
8944
8945 void PrintOptions(std::ostream& os) const;
8946};
8947
8948struct Simd256ShufpsOp : FixedArityOperationT<2, Simd256ShufpsOp> {
8949 static constexpr OpEffects effects = OpEffects();
8950 uint8_t control;
8951
8952 base::Vector<const RegisterRepresentation> outputs_rep() const {
8953 return RepVector<RegisterRepresentation::Simd256()>();
8954 }
8955
8956 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
8957 ZoneVector<MaybeRegisterRepresentation>& storage) const {
8958 return MaybeRepVector<RegisterRepresentation::Simd256(),
8959 RegisterRepresentation::Simd256()>();
8960 }
8961
8962 Simd256ShufpsOp(V<Simd256> left, V<Simd256> right, uint8_t control)
8963 : Base(left, right), control(control) {}
8964
8965 V<Simd256> left() const { return Base::input<Simd256>(0); }
8966 V<Simd256> right() const { return Base::input<Simd256>(1); }
8967
8968
8969 auto options() const { return std::tuple{control}; }
8970
8971 void PrintOptions(std::ostream& os) const;
8972};
8973
8974#define FOREACH_SIMD_256_UNPACK_OPCODE(V) \
8975 V(32x8Low) \
8976 V(32x8High)
8977struct Simd256UnpackOp : FixedArityOperationT<2, Simd256UnpackOp> {
8978 enum class Kind : uint8_t {
8979#define DEFINE_KIND(kind) k##kind,
8980 FOREACH_SIMD_256_UNPACK_OPCODE(DEFINE_KIND)
8981#undef DEFINE_KIND
8982 };
8983 static constexpr OpEffects effects = OpEffects();
8984 Kind kind;
8985
8986 base::Vector<const RegisterRepresentation> outputs_rep() const {
8987 return RepVector<RegisterRepresentation::Simd256()>();
8988 }
8989
8990 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
8991 ZoneVector<MaybeRegisterRepresentation>& storage) const {
8992 return MaybeRepVector<RegisterRepresentation::Simd256(),
8993 RegisterRepresentation::Simd256()>();
8994 }
8995
8996 Simd256UnpackOp(V<Simd256> left, V<Simd256> right, Kind kind)
8997 : Base(left, right), kind(kind) {}
8998
8999 V<Simd256> left() const { return Base::input<Simd256>(0); }
9000 V<Simd256> right() const { return Base::input<Simd256>(1); }
9001
9002
9003 auto options() const { return std::tuple{kind}; }
9004};
9005std::ostream& operator<<(std::ostream& os, Simd256UnpackOp::Kind kind);
9006#endif // V8_TARGET_ARCH_X64
9007
9008#endif // V8_ENABLE_WASM_SIMD256_REVEC
9009
9010struct LoadStackPointerOp : FixedArityOperationT<0, LoadStackPointerOp> {
9011 // TODO(nicohartmann@): Review effects.
9012 static constexpr OpEffects effects = OpEffects().CanReadMemory();
9013
9014 base::Vector<const RegisterRepresentation> outputs_rep() const {
9015 return RepVector<RegisterRepresentation::WordPtr()>();
9016 }
9017
9018 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
9019 ZoneVector<MaybeRegisterRepresentation>& storage) const {
9020 return {};
9021 }
9022
9023 auto options() const { return std::tuple{}; }
9024};
9025
9026struct SetStackPointerOp : FixedArityOperationT<1, SetStackPointerOp> {
9027 // TODO(nicohartmann@): Review effects.
9028 static constexpr OpEffects effects = OpEffects().CanCallAnything();
9029
9030 OpIndex value() const { return Base::input(0); }
9031
9032 explicit SetStackPointerOp(OpIndex value) : Base(value) {}
9033
9034 base::Vector<const RegisterRepresentation> outputs_rep() const { return {}; }
9035
9036 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
9037 ZoneVector<MaybeRegisterRepresentation>& storage) const {
9038 return MaybeRepVector<MaybeRegisterRepresentation::WordPtr()>();
9039 }
9040
9041 auto options() const { return std::tuple{}; }
9042};
9043
9044#endif // V8_ENABLE_WEBASSEMBLY
9045
9046#ifdef V8_ENABLE_CONTINUATION_PRESERVED_EMBEDDER_DATA
9047struct GetContinuationPreservedEmbedderDataOp
9048 : FixedArityOperationT<0, GetContinuationPreservedEmbedderDataOp> {
9049 static constexpr OpEffects effects = OpEffects().CanReadOffHeapMemory();
9050
9051 base::Vector<const RegisterRepresentation> outputs_rep() const {
9052 return RepVector<RegisterRepresentation::Tagged()>();
9053 }
9054
9055 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
9056 ZoneVector<MaybeRegisterRepresentation>& storage) const {
9057 return {};
9058 }
9059
9060 GetContinuationPreservedEmbedderDataOp() : Base() {}
9061
9062
9063 auto options() const { return std::tuple{}; }
9064};
9065
9066struct SetContinuationPreservedEmbedderDataOp
9067 : FixedArityOperationT<1, SetContinuationPreservedEmbedderDataOp> {
9068 static constexpr OpEffects effects = OpEffects().CanWriteOffHeapMemory();
9069
9070 base::Vector<const RegisterRepresentation> outputs_rep() const { return {}; }
9071
9072 base::Vector<const MaybeRegisterRepresentation> inputs_rep(
9073 ZoneVector<MaybeRegisterRepresentation>& storage) const {
9074 return MaybeRepVector<MaybeRegisterRepresentation::Tagged()>();
9075 }
9076
9077 explicit SetContinuationPreservedEmbedderDataOp(V<Object> value)
9078 : Base(value) {}
9079
9080
9081 auto options() const { return std::tuple{}; }
9082};
9083#endif // V8_ENABLE_CONTINUATION_PRESERVED_EMBEDDER_DATA
9084
9085#define OPERATION_EFFECTS_CASE(Name) Name##Op::EffectsIfStatic(),
9086static constexpr std::optional<OpEffects>
9089#undef OPERATION_EFFECTS_CASE
9090
9091template <class Op>
9093
9094template <Opcode opcode>
9096
9097#define OPERATION_OPCODE_MAP_CASE(Name) \
9098 template <> \
9099 struct opcode_to_operation_map<Opcode::k##Name> { \
9100 using Op = Name##Op; \
9101 };
9103#undef OPERATION_OPCODE_MAP_CASE
9104
9105template <class Op, class = void>
9106struct static_operation_input_count : std::integral_constant<uint32_t, 0> {};
9107template <class Op>
9108struct static_operation_input_count<Op, std::void_t<decltype(Op::inputs)>>
9109 : std::integral_constant<uint32_t, sizeof(Op::inputs) / sizeof(OpIndex)> {};
9111#define OPERATION_SIZE(Name) sizeof(Name##Op),
9113#undef OPERATION_SIZE
9114};
9116#define OPERATION_SIZE(Name) (sizeof(Name##Op) / sizeof(OpIndex)),
9118#undef OPERATION_SIZE
9119};
9120
9121inline base::Vector<const OpIndex> Operation::inputs() const {
9122 // This is actually undefined behavior, since we use the `this` pointer to
9123 // access an adjacent object.
9124 const OpIndex* ptr = reinterpret_cast<const OpIndex*>(
9125 reinterpret_cast<const char*>(this) +
9127 return {ptr, input_count};
9128}
9129
9130inline OpEffects Operation::Effects() const {
9131 if (auto prop = kOperationEffectsTable[OpcodeIndex(opcode)]) {
9132 return *prop;
9133 }
9134 switch (opcode) {
9135 case Opcode::kLoad:
9136 return Cast<LoadOp>().Effects();
9137 case Opcode::kStore:
9138 return Cast<StoreOp>().Effects();
9139 case Opcode::kCall:
9140 return Cast<CallOp>().Effects();
9141 case Opcode::kDidntThrow:
9142 return Cast<DidntThrowOp>().Effects();
9143 case Opcode::kTaggedBitcast:
9144 return Cast<TaggedBitcastOp>().Effects();
9145 case Opcode::kAtomicRMW:
9146 return Cast<AtomicRMWOp>().Effects();
9147 case Opcode::kAtomicWord32Pair:
9148 return Cast<AtomicWord32PairOp>().Effects();
9149 case Opcode::kJSStackCheck:
9150 return Cast<JSStackCheckOp>().Effects();
9151#if V8_ENABLE_WEBASSEMBLY
9152 case Opcode::kWasmStackCheck:
9153 return Cast<WasmStackCheckOp>().Effects();
9154 case Opcode::kStructGet:
9155 return Cast<StructGetOp>().Effects();
9156 case Opcode::kStructSet:
9157 return Cast<StructSetOp>().Effects();
9158 case Opcode::kArrayLength:
9159 return Cast<ArrayLengthOp>().Effects();
9160 case Opcode::kSimd128LaneMemory:
9161 return Cast<Simd128LaneMemoryOp>().Effects();
9162 case Opcode::kSimd128LoadTransform:
9163 return Cast<Simd128LoadTransformOp>().Effects();
9164#if V8_ENABLE_WASM_DEINTERLEAVED_MEM_OPS
9165 case Opcode::kSimd128LoadPairDeinterleave:
9166 return Cast<Simd128LoadPairDeinterleaveOp>().Effects();
9167#endif // V8_ENABLE_WASM_DEINTERLEAVED_MEM_OPS
9168#if V8_ENABLE_WASM_SIMD256_REVEC
9169 case Opcode::kSimd256LoadTransform:
9170 return Cast<Simd256LoadTransformOp>().Effects();
9171#endif // V8_ENABLE_WASM_SIMD256_REVEC
9172#endif
9173 default:
9174 UNREACHABLE();
9175 }
9176}
9177
9178// static
9179inline size_t Operation::StorageSlotCount(Opcode opcode, size_t input_count) {
9181 constexpr size_t r = sizeof(OperationStorageSlot) / sizeof(OpIndex);
9182 static_assert(sizeof(OperationStorageSlot) % sizeof(OpIndex) == 0);
9183 return std::max<size_t>(2, (r - 1 + size + input_count) / r);
9184}
9185
9187 return op.Is<FrameStateOp>() || op.outputs_rep().size() > 0;
9188}
9189
9190inline base::Vector<const RegisterRepresentation> Operation::outputs_rep()
9191 const {
9192 switch (opcode) {
9193#define CASE(type) \
9194 case Opcode::k##type: { \
9195 const type##Op& op = Cast<type##Op>(); \
9196 return op.outputs_rep(); \
9197 }
9199#undef CASE
9200 }
9201}
9202
9205 switch (opcode) {
9206#define CASE(type) \
9207 case Opcode::k##type: { \
9208 const type##Op& op = Cast<type##Op>(); \
9209 return op.inputs_rep(storage); \
9210 }
9212#undef CASE
9213 }
9214}
9215
9216bool IsUnlikelySuccessor(const Block* block, const Block* successor,
9217 const Graph& graph);
9218
9219// Analyzers should skip (= ignore) operations for which ShouldSkipOperation
9220// returns true. This happens for:
9221// - DeadOp: this means that a previous Analyzer decided that this operation is
9222// dead.
9223// - Operations that are not RequiredWhenUnused, and whose saturated_use_count
9224// is 0: this corresponds to pure operations that have no uses.
9226 if (op.Is<DeadOp>()) return true;
9227 return op.saturated_use_count.IsZero() && !op.IsRequiredWhenUnused();
9228}
9229
9230namespace detail {
9231// Defining `input_count` to compute the number of OpIndex inputs of an
9232// operation.
9233
9234// There is one overload for each possible type of parameters for all
9235// Operations rather than a default generic overload, so that we don't
9236// accidentally forget some types (eg, if a new Operation takes its inputs as a
9237// std::vector<OpIndex>, we shouldn't count this as "0 inputs because it's
9238// neither raw OpIndex nor base::Vector<OpIndex>", which a generic overload
9239// might do).
9240
9241// Base case
9242constexpr size_t input_count() { return 0; }
9243
9244// All parameters that are not OpIndex and should thus not count towards the
9245// "input_count" of the operations.
9246template <typename T>
9247constexpr size_t input_count(T)
9248 requires(std::is_enum_v<T> || std::is_integral_v<T> ||
9249 std::is_floating_point_v<T>)
9250{
9251 return 0;
9252}
9253// TODO(42203211): The first parameter should be just DirectHandle<T> and
9254// MaybeDirectHandle<T> but now it does not compile with implicit Handle to
9255// DirectHandle conversions.
9256template <template <typename> typename HandleType, typename T>
9257constexpr size_t input_count(const HandleType<T>)
9258 requires(std::disjunction_v<
9259 std::is_convertible<HandleType<T>, DirectHandle<T>>,
9260 std::is_convertible<HandleType<T>, MaybeDirectHandle<T>>>)
9261{
9262 return 0;
9263}
9264template <typename T>
9265constexpr size_t input_count(const base::Flags<T>) {
9266 return 0;
9267}
9268constexpr size_t input_count(const Block*) { return 0; }
9269constexpr size_t input_count(const TSCallDescriptor*) { return 0; }
9270constexpr size_t input_count(const char*) { return 0; }
9271constexpr size_t input_count(const DeoptimizeParameters*) { return 0; }
9272constexpr size_t input_count(const FastApiCallParameters*) { return 0; }
9273constexpr size_t input_count(const FrameStateData*) { return 0; }
9274constexpr size_t input_count(const base::Vector<SwitchOp::Case>) { return 0; }
9275constexpr size_t input_count(LoadOp::Kind) { return 0; }
9276constexpr size_t input_count(RegisterRepresentation) { return 0; }
9277constexpr size_t input_count(MemoryRepresentation) { return 0; }
9278constexpr size_t input_count(OpEffects) { return 0; }
9279constexpr size_t input_count(ExternalPointerTagRange) { return 0; }
9280inline size_t input_count(const ElementsTransition) { return 0; }
9282 return 0;
9283}
9284inline size_t input_count(const FeedbackSource) { return 0; }
9285inline size_t input_count(const ZoneRefSet<Map>) { return 0; }
9286inline size_t input_count(ConstantOp::Storage) { return 0; }
9287inline size_t input_count(Type) { return 0; }
9289 return 0;
9290}
9291#ifdef V8_ENABLE_WEBASSEMBLY
9292constexpr size_t input_count(const wasm::WasmGlobal*) { return 0; }
9293constexpr size_t input_count(const wasm::StructType*) { return 0; }
9294constexpr size_t input_count(const wasm::ArrayType*) { return 0; }
9295constexpr size_t input_count(wasm::ValueType) { return 0; }
9296constexpr size_t input_count(WasmTypeCheckConfig) { return 0; }
9297constexpr size_t input_count(wasm::ModuleTypeIndex) { return 0; }
9298#endif
9299
9300// All parameters that are OpIndex-like (ie, OpIndex, and OpIndex containers)
9301constexpr size_t input_count(OpIndex) { return 1; }
9302constexpr size_t input_count(OptionalOpIndex) { return 1; }
9304 return inputs.size();
9305}
9306template <typename T>
9307constexpr size_t input_count(base::Vector<const V<T>> inputs) {
9308 return inputs.size();
9309}
9310} // namespace detail
9311
9312template <typename Op, typename... Args>
9314 Args... args) {
9315 size_t input_count = (0 + ... + detail::input_count(args));
9316 size_t size = Operation::StorageSlotCount(Op::opcode, input_count);
9317 storage.resize(size);
9318 Op* op = new (storage.data()) Op(args...);
9319 // Checking that the {input_count} we computed is at least the actual
9320 // input_count of the operation. {input_count} could be greater in the case of
9321 // OptionalOpIndex: they count for 1 input when computing {input_count} here,
9322 // but in Operations, they only count for 1 input when they are valid.
9323 DCHECK_GE(input_count, op->input_count);
9324 return op;
9325}
9326
9327template <typename F>
9328auto VisitOperation(const Operation& op, F&& f) {
9329 switch (op.opcode) {
9330#define CASE(name) \
9331 case Opcode::k##name: \
9332 return f(op.Cast<name##Op>());
9334#undef CASE
9335 }
9336}
9337
9338// Checking that throwing operations have the required members and options.
9339namespace details {
9340
9341template <typename T, typename Tuple>
9343
9344template <typename T, typename... Ts>
9345struct TupleHasType<T, std::tuple<Ts...>> {
9346 static constexpr bool value = (std::is_same_v<T, Ts> || ...);
9347};
9348
9349template <typename Op, typename = void>
9350struct ThrowingOpHasProperMembers : std::false_type {};
9351template <typename Op>
9353 Op, std::void_t<std::conjunction<decltype(Op::kOutputRepsStorage),
9354 decltype(Op::lazy_deopt_on_throw)>>>
9355 : std::true_type {};
9356
9357template <typename Op, typename = void>
9358struct ThrowingOpHasLazyDeoptOption : std::false_type {};
9359
9360template <typename Op>
9362 Op, std::enable_if_t<TupleHasType<
9363 LazyDeoptOnThrow, decltype(std::declval<Op>().options())>::value>>
9364 : std::true_type {};
9365
9366// CallOp has special handling because its outputs_rep are dynamic (and found on
9367// its call descriptor).
9368template <>
9369struct ThrowingOpHasLazyDeoptOption<CallOp, void> : std::true_type {};
9370template <>
9371struct ThrowingOpHasProperMembers<CallOp, void> : std::true_type {};
9372
9373template <>
9374struct ThrowingOpHasProperMembers<FastApiCallOp, void> : std::true_type {};
9375} // namespace details
9376
9377#define THROWING_OP_LOOKS_VALID(Name) \
9378 static_assert(details::ThrowingOpHasProperMembers<Name##Op>()); \
9379 static_assert(details::ThrowingOpHasLazyDeoptOption<Name##Op>());
9381#undef THROWING_OP_LOOKS_VALID
9382
9383} // namespace v8::internal::compiler::turboshaft
9384
9385#endif // V8_COMPILER_TURBOSHAFT_OPERATIONS_H_
#define V(Name)
#define F(name, str)
#define T
#define CASE(Name,...)
Builtins::Kind kind
Definition builtins.cc:40
#define FORWARD_DECLARE(Name, Argc)
Definition builtins.cc:30
int default_case
constexpr Type GetType() const
constexpr Flags GetFlags() const
constexpr SequenceType GetSequenceType() const
void resize(size_t new_size)
Vector< T > SubVector(size_t from, size_t to) const
Definition vector.h:41
constexpr size_t size() const
Definition vector.h:70
V8_INLINE Address address() const
Definition handles.h:73
constexpr MachineRepresentation representation() const
bool equals(MaybeHandle< T > other) const
V8_INLINE Address address() const
void resize(size_t new_size)
MachineType GetParameterType(size_t index) const
Definition linkage.h:290
MachineType GetReturnType(size_t index) const
Definition linkage.h:281
static constexpr const MaybeRegisterRepresentation * ToMaybeRepPointer(RegisterRepresentation rep)
Definition operations.h:493
static constexpr base::Vector< const MaybeRegisterRepresentation > PairOf(RegisterRepresentation rep)
Definition operations.h:487
static constexpr base::Vector< const MaybeRegisterRepresentation > SingleRep(RegisterRepresentation rep)
Definition operations.h:482
static constexpr MaybeRegisterRepresentation rep_map[]
Definition operations.h:501
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 MaybeRegisterRepresentation Simd256()
RegisterRepresentation ToRegisterRepresentationForStore() const
SaturatedUint8 & operator+=(const SaturatedUint8 &other)
Definition operations.h:900
static SaturatedUint8 FromSize(size_t value)
Definition operations.h:907
base::Mutex & mutex_
#define DEFINE_MULTI_SWITCH_INTEGRAL(name, max_value)
Definition utils.h:156
constexpr const char * ToString(DataViewOp op)
int start
int end
base::Vector< const DirectHandle< Object > > args
Definition execution.cc:74
too high values may cause the compiler to set high thresholds for inlining to as much as possible avoid inlined allocation of objects that cannot escape trace load stores from virtual maglev objects use TurboFan fast string builder analyze liveness of environment slots and zap dead values trace TurboFan load elimination emit data about basic block usage in builtins to this enable builtin reordering when run mksnapshot flag for emit warnings when applying builtin profile data verify register allocation in TurboFan randomly schedule instructions to stress dependency tracking enable store store elimination in TurboFan rewrite far to near simulate GC compiler thread race related to allow float parameters to be passed in simulator mode JS Wasm Run additional turbo_optimize_inlined_js_wasm_wrappers enable experimental feedback collection in generic lowering enable Turboshaft s WasmLoadElimination enable Turboshaft s low level load elimination for JS enable Turboshaft s escape analysis for string concatenation use enable Turbolev features that we want to ship in the not too far future trace individual Turboshaft reduction steps trace intermediate Turboshaft reduction steps invocation count threshold for early optimization Enables optimizations which favor memory size over execution speed Enables sampling allocation profiler with X as a sample interval min size of a semi the new space consists of two semi spaces max size of the Collect garbage after Collect garbage after keeps maps alive for< n > old space garbage collections print one detailed trace line in allocation gc speed threshold for starting incremental marking via a task in percent of available threshold for starting incremental marking immediately in percent of available Use a single schedule for determining a marking schedule between JS and C objects schedules the minor GC task with kUserVisible priority max worker number of concurrent for NumberOfWorkerThreads start background threads that allocate memory concurrent_array_buffer_sweeping use parallel threads to clear weak refs in the atomic pause trace progress of the incremental marking trace object counts and memory usage report a tick only when allocated zone memory changes by this amount TracingFlags::gc_stats TracingFlags::gc_stats track native contexts that are expected to be garbage collected verify heap pointers before and after GC memory reducer runs GC with ReduceMemoryFootprint flag Maximum number of memory reducer GCs scheduled Old gen GC speed is computed directly from gc tracer counters Perform compaction on full GCs based on V8 s default heuristics Perform compaction on every full GC Perform code space compaction when finalizing a full GC with stack Stress GC compaction to flush out bugs with moving objects flush of baseline code when it has not been executed recently Use time base code flushing instead of age Use a progress bar to scan large objects in increments when incremental marking is active force incremental marking for small heaps and run it more often force marking at random points between and force scavenge at random points between and reclaim otherwise unreachable unmodified wrapper objects when possible less compaction in non memory reducing mode use high priority threads for concurrent Marking Test mode only flag It allows an unit test to select evacuation candidates use incremental marking for CppHeap cppheap_concurrent_marking c value for membalancer A special constant to balance between memory and space tradeoff The smaller the more memory it uses enable use of SSE4 instructions if available enable use of AVX VNNI instructions if available enable use of POPCNT instruction if available force all emitted branches to be in long mode(MIPS/PPC only)") DEFINE_BOOL(partial_constant_pool
other heap size flags(e.g. initial_heap_size) take precedence") DEFINE_SIZE_T( max_shared_heap_size
Zone * graph_zone
JSHeapBroker * broker
OptionalOpIndex index
int32_t offset
TNode< Context > context
#define DECLARE_GETTER(name, Type)
Definition js-graph.h:151
#define DECLARE_FIELD(name,...)
Definition js-graph.h:167
double second
DirectHandle< JSReceiver > options
ZoneVector< RpoNumber > & result
EmitFn fn
Point from
int x
Point to
int position
Definition liveedit.cc:290
InstructionOperand destination
int r
Definition mul-fft.cc:298
STL namespace.
int int32_t
Definition unicode.cc:40
V8_INLINE Dest bit_cast(Source const &source)
Definition macros.h:95
constexpr Vector< T > VectorOf(T *start, size_t size)
Definition vector.h:360
std::ostream & operator<<(std::ostream &os, PaddingSpace padding)
constexpr uint16_t kNumberOfBlockTerminatorOpcodes
Definition operations.h:407
constexpr Opcode operation_to_opcode_v
Definition operations.h:397
V8_INLINE const Operation & Get(const Graph &graph, OpIndex index)
Definition graph.h:1231
constexpr bool MayThrow(Opcode opcode)
Definition operations.h:432
typename underlying_operation< T >::type underlying_operation_t
Definition operations.h:929
constexpr RegisterRepresentation RegisterRepresentationForArrayType(ExternalArrayType array_type)
V8_INLINE size_t fast_hash_combine()
Definition fast-hash.h:19
const char * OpcodeName(Opcode opcode)
V8_EXPORT_PRIVATE V8_INLINE bool ShouldSkipOperation(const Operation &op)
V8_INLINE OperationStorageSlot * AllocateOpStorage(Graph *graph, size_t slot_count)
Definition graph.h:1226
V8_INLINE size_t hash_value(OpIndex op)
Definition index.h:773
std::optional< Builtin > TryGetBuiltinId(const ConstantOp *target, JSHeapBroker *broker)
Definition operations.cc:45
auto VisitOperation(const Operation &op, F &&f)
base::Vector< const RegisterRepresentation > RepVector()
constexpr bool IsBlockTerminator(Opcode opcode)
Definition operations.h:416
base::Vector< const RegisterRepresentation > VectorForRep(RegisterRepresentation rep)
V8_INLINE bool CanBeUsedAsInput(const Operation &op)
Zone * get_zone(Graph *graph)
Definition operations.cc:43
OptionalV(V< T >) -> OptionalV< T >
constexpr std::underlying_type_t< Opcode > OpcodeIndex(Opcode x)
Definition operations.h:373
base::Vector< const MaybeRegisterRepresentation > MaybeRepVector()
bool CannotSwapProtectedLoads(OpEffects first, OpEffects second)
Definition operations.h:858
constexpr size_t kOperationSizeTable[kNumberOfOpcodes]
constexpr uint16_t kNumberOfOpcodes
Definition operations.h:412
static constexpr std::optional< OpEffects > kOperationEffectsTable[kNumberOfOpcodes]
constexpr size_t kOperationSizeDividedBySizeofOpIndexTable[kNumberOfOpcodes]
constexpr char kCompilationZoneName[]
Definition operations.h:60
bool CannotSwapOperations(OpEffects first, OpEffects second)
Definition operations.h:854
base::Vector< T > InitVectorOf(ZoneVector< T > &storage, std::initializer_list< RegisterRepresentation > values)
Definition operations.h:469
base::SmallVector< Block *, 4 > SuccessorBlocks(const Block &block, const Graph &graph)
bool IsUnlikelySuccessor(const Block *block, const Block *successor, const Graph &graph)
Op * CreateOperation(base::SmallVector< OperationStorageSlot, 32 > &storage, Args... args)
V8_INLINE IndirectHandle< T > handle(Tagged< T > object, Isolate *isolate)
Definition handles-inl.h:72
constexpr int kSimd128Size
Definition globals.h:706
Tagged(T object) -> Tagged< T >
constexpr int kIndirectPointerTagShift
bool IsZero(Tagged< Object > obj)
std::ostream & operator<<(std::ostream &os, AtomicMemoryOrder order)
constexpr int kSimd256Size
Definition globals.h:709
constexpr bool SmiValuesAre31Bits()
const int kHeapObjectTag
Definition v8-internal.h:72
@ kExternalFloat64Array
Definition globals.h:2461
@ kExternalUint32Array
Definition globals.h:2458
@ kExternalBigInt64Array
Definition globals.h:2463
@ kExternalInt32Array
Definition globals.h:2457
@ kExternalInt8Array
Definition globals.h:2453
@ kExternalUint8ClampedArray
Definition globals.h:2462
@ kExternalUint8Array
Definition globals.h:2454
@ kExternalUint16Array
Definition globals.h:2456
@ kExternalFloat32Array
Definition globals.h:2460
@ kExternalInt16Array
Definition globals.h:2455
@ kExternalFloat16Array
Definition globals.h:2459
@ kExternalBigUint64Array
Definition globals.h:2464
constexpr VFPRoundingMode kRoundToZero
return value
Definition map-inl.h:893
constexpr bool Is64()
constexpr bool IsIntegral(MachineRepresentation rep)
bool is_signed(Condition cond)
!IsContextMap !IsContextMap native_context
Definition map-inl.h:877
std::ostream & operator<<(std::ostream &os, const Operation &operation)
Definition operation.h:49
#define NON_TERMINATOR_CASE(op)
#define TURBOSHAFT_THROWING_OPERATIONS_LIST(V)
Definition operations.h:426
#define TURBOSHAFT_OPERATION_LIST_BLOCK_TERMINATOR(V)
Definition operations.h:211
#define COUNT_OPCODES(Name)
Definition operations.h:406
#define THROWING_OP_LOOKS_VALID(Name)
#define GENERIC_BINOP_LIST(V)
#define SUPPORTED_OPERATIONS_LIST(V)
#define OPERATION_OPCODE_MAP_CASE(Name)
Definition operations.h:385
#define DEFINE_KIND(Name)
#define OPERATION_SIZE(Name)
#define TURBOSHAFT_OPERATION_LIST(V)
Definition operations.h:362
#define TURBOSHAFT_OPERATION_LIST_NOT_BLOCK_TERMINATOR(V)
Definition operations.h:354
#define THROWING_OP_BOILERPLATE(...)
Definition operations.h:459
#define GENERIC_UNOP_LIST(V)
#define OPERATION_EFFECTS_CASE(Name)
#define I(name, number_of_args, result_size)
Definition runtime.cc:36
#define NON_EXPORTED_BASE(code)
#define UNREACHABLE()
Definition logging.h:67
#define DCHECK_LE(v1, v2)
Definition logging.h:490
#define DCHECK_IMPLIES(v1, v2)
Definition logging.h:493
#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_LT(v1, v2)
Definition logging.h:489
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define DCHECK_GT(v1, v2)
Definition logging.h:487
#define USE(...)
Definition macros.h:293
#define V8_EXPORT_PRIVATE
Definition macros.h:460
#define arraysize(array)
Definition macros.h:67
constexpr bool IsEmpty() const
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
AllocateOp(V< WordPtr > size, AllocationType type)
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
ArgumentsLengthOp(Kind kind, int formal_parameter_count)
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const RegisterRepresentation > outputs_rep() const
AssumeMapOp(V< HeapObject > heap_object, ZoneRefSet< Map > maps)
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
AtomicRMWOp(OpIndex base, OpIndex index, OpIndex value, OptionalOpIndex expected, BinOp bin_op, RegisterRepresentation in_out_rep, MemoryRepresentation memory_rep, MemoryAccessKind kind)
void Validate(const Graph &graph) const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const RegisterRepresentation > outputs_rep() const
static AtomicRMWOp & New(Graph *graph, OpIndex base, OpIndex index, OpIndex value, OptionalOpIndex expected, BinOp bin_op, RegisterRepresentation result_rep, MemoryRepresentation input_rep, MemoryAccessKind kind)
V8_INLINE auto Explode(Fn fn, Mapper &mapper) const
static AtomicWord32PairOp & New(Graph *graph, V< WordPtr > base, OptionalV< WordPtr > index, OptionalV< Word32 > value_low, OptionalV< Word32 > value_high, OptionalV< Word32 > expected_low, OptionalV< Word32 > expected_high, Kind kind, int32_t offset)
V8_INLINE auto Explode(Fn fn, Mapper &mapper) const
base::Vector< const RegisterRepresentation > outputs_rep() const
static constexpr size_t InputCount(Kind kind, bool has_index)
static Kind KindFromBinOp(AtomicRMWOp::BinOp bin_op)
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
AtomicWord32PairOp(V< WordPtr > base, OptionalV< WordPtr > index, OptionalV< Word32 > value_low, OptionalV< Word32 > value_high, OptionalV< Word32 > expected_low, OptionalV< Word32 > expected_high, Kind kind, int32_t offset)
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
BigIntBinopOp(V< BigInt > left, V< BigInt > right, V< FrameState > frame_state, Kind kind)
base::Vector< const RegisterRepresentation > outputs_rep() const
BigIntComparisonOp(V< BigInt > left, V< BigInt > right, Kind kind)
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
BigIntUnaryOp(V< BigInt > input, Kind kind)
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
BitcastWord32PairToFloat64Op(V< Word32 > high_word32, V< Word32 > low_word32)
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
BranchOp(V< Word32 > condition, Block *if_true, Block *if_false, BranchHint hint)
base::Vector< const RegisterRepresentation > outputs_rep() const
void Validate(const Graph &graph) const
base::Vector< const OpIndex > arguments() const
static CallOp & New(Graph *graph, V< CallTarget > callee, OptionalV< FrameState > frame_state, base::Vector< const OpIndex > arguments, const TSCallDescriptor *descriptor, OpEffects effects)
CallOp(V< CallTarget > callee, OptionalV< FrameState > frame_state, base::Vector< const OpIndex > arguments, const TSCallDescriptor *descriptor, OpEffects effects)
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const RegisterRepresentation > results_rep() const
V8_INLINE auto Explode(Fn fn, Mapper &mapper) const
OptionalV< FrameState > frame_state() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
ChangeOp(V< Untagged > input, Kind kind, Assumption assumption, RegisterRepresentation from, RegisterRepresentation to)
void Validate(const Graph &graph) const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
static bool IsReversible(Kind kind, Assumption assumption, RegisterRepresentation from, RegisterRepresentation to, Kind reverse_kind, bool signalling_nan_possible)
bool IsReversibleBy(Kind reverse_kind, bool signalling_nan_possible) const
base::Vector< const RegisterRepresentation > outputs_rep() const
ChangeOrDeoptOp(V< Untagged > input, V< FrameState > frame_state, Kind kind, CheckForMinusZeroMode minus_zero_mode, const FeedbackSource &feedback)
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
CheckEqualsInternalizedStringOp(V< Object > expected, V< Object > value, V< FrameState > frame_state)
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
CheckExceptionOp(V< Any > throwing_operation, Block *successor, Block *catch_block)
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const RegisterRepresentation > outputs_rep() const
static CheckMapsOp & New(Graph *graph, V< HeapObject > heap_object, V< FrameState > frame_state, OptionalV< Map > map, ZoneRefSet< Map > maps, CheckMapsFlags flags, const FeedbackSource &feedback)
CheckMapsOp(V< HeapObject > heap_object, V< FrameState > frame_state, OptionalV< Map > map, ZoneRefSet< Map > maps, CheckMapsFlags flags, const FeedbackSource &feedback)
V8_INLINE auto Explode(Fn fn, Mapper &mapper) const
void Validate(const Graph &graph) const
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
CheckTurboshaftTypeOfOp(OpIndex input, RegisterRepresentation rep, Type type, bool successful)
CheckedClosureOp(V< Object > input, V< FrameState > frame_state, Handle< FeedbackCell > feedback_cell)
bool operator==(const CheckedClosureOp &other) const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const RegisterRepresentation > outputs_rep() const
size_t hash_value(HashingStrategy strategy=HashingStrategy::kDefault) const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const RegisterRepresentation > outputs_rep() const
CompareMapsOp(V< HeapObject > heap_object, OptionalV< Map > map, ZoneRefSet< Map > maps)
V8_INLINE auto Explode(Fn fn, Mapper &mapper) const
static CompareMapsOp & New(Graph *graph, V< HeapObject > heap_object, OptionalV< Map > map, ZoneRefSet< Map > maps)
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
void Validate(const Graph &graph) const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
ComparisonOp(V< Any > left, V< Any > right, Kind kind, RegisterRepresentation rep)
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const RegisterRepresentation > outputs_rep() const
size_t hash_value(HashingStrategy strategy=HashingStrategy::kDefault) const
IndirectHandle< i::HeapObject > handle() const
void Validate(const Graph &graph) const
bool operator==(const ConstantOp &other) const
static RegisterRepresentation Representation(Kind kind)
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &) const
ConvertJSPrimitiveToObjectOp(V< JSPrimitive > value, V< Context > native_context, V< JSGlobalProxy > global_proxy, ConvertReceiverMode mode)
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const RegisterRepresentation > outputs_rep() const
ConvertJSPrimitiveToUntaggedOp(V< JSPrimitive > input, UntaggedKind kind, InputAssumptions input_assumptions)
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
ConvertJSPrimitiveToUntaggedOrDeoptOp(V< Object > input, V< FrameState > frame_state, JSPrimitiveKind from_kind, UntaggedKind to_kind, CheckForMinusZeroMode minus_zero_mode, const FeedbackSource &feedback)
ConvertOp(V< Object > input, Kind from, Kind to)
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
ConvertUntaggedToJSPrimitiveOp(V< Untagged > input, JSPrimitiveKind kind, RegisterRepresentation input_rep, InputInterpretation input_interpretation, CheckForMinusZeroMode minus_zero_mode)
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
ConvertUntaggedToJSPrimitiveOrDeoptOp(V< Untagged > input, V< FrameState > frame_state, JSPrimitiveKind kind, RegisterRepresentation input_rep, InputInterpretation input_interpretation, const FeedbackSource &feedback)
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
DebugPrintOp(OpIndex input, RegisterRepresentation rep)
DecodeExternalPointerOp(OpIndex handle, ExternalPointerTagRange tag_range)
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
size_t hash_value(HashingStrategy strategy=HashingStrategy::kDefault) const
bool EqualsForGVN(const DeoptimizeIfOp &other) const
DeoptimizeIfOp(V< Word32 > condition, V< FrameState > frame_state, bool negated, const DeoptimizeParameters *parameters)
void Validate(const Graph &graph) const
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
DeoptimizeOp(V< FrameState > frame_state, const DeoptimizeParameters *parameters)
const base::Vector< const RegisterRepresentation > * results_rep
DidntThrowOp(OpIndex throwing_operation, bool has_catch_block, const base::Vector< const RegisterRepresentation > *results_rep, OpEffects throwing_op_effects)
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const RegisterRepresentation > outputs_rep() const
static EffectDimensions FromBits(Bits bits)
Definition operations.h:565
bool operator!=(EffectDimensions other) const
Definition operations.h:571
bool operator==(EffectDimensions other) const
Definition operations.h:568
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const RegisterRepresentation > outputs_rep() const
MaybeRegisterRepresentation argument_representation(unsigned argument_index) const
static FastApiCallOp & New(Graph *graph, V< FrameState > frame_state, V< Object > data_argument, V< Context > context, base::Vector< const OpIndex > arguments, const FastApiCallParameters *parameters, base::Vector< const RegisterRepresentation > out_reps)
base::Vector< const RegisterRepresentation > out_reps
base::Vector< const OpIndex > arguments() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
V8_INLINE auto Explode(Fn fn, Mapper &mapper) const
base::Vector< const RegisterRepresentation > outputs_rep() const
FastApiCallOp(V< FrameState > frame_state, V< Object > data_argument, V< Context > context, base::Vector< const OpIndex > arguments, const FastApiCallParameters *parameters, base::Vector< const RegisterRepresentation > out_reps)
static const FastApiCallParameters * Create(FastApiCallFunction c_function, Zone *graph_zone)
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const RegisterRepresentation > outputs_rep() const
FindOrderedHashEntryOp(OpIndex data_structure, V< Word32 > key, Kind kind)
V8_INLINE auto ExplodeImpl(Fn fn, Mapper &mapper, std::index_sequence< InputI... >, std::index_sequence< OptionI... >) const
static Derived & New(Graph *graph, Args... args)
V8_INLINE auto Explode(Fn fn, Mapper &mapper) const
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
Float64IsOp(V< Float64 > input, NumericKind kind)
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
Float64SameValueOp(V< Float64 > left, V< Float64 > right)
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const RegisterRepresentation > outputs_rep() const
void Validate(const Graph &graph) const
FloatBinopOp(V< Float > left, V< Float > right, Kind kind, FloatRepresentation rep)
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
FloatUnaryOp(V< Float > input, Kind kind, FloatRepresentation rep)
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const RegisterRepresentation > outputs_rep() const
const OpIndex state_value(size_t idx) const
FrameStateOp(base::Vector< const OpIndex > inputs, bool inlined, const FrameStateData *data)
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
V8_INLINE auto Explode(Fn fn, Mapper &mapper) const
base::Vector< const OpIndex > state_values() const
RegisterRepresentation state_value_rep(size_t idx) const
size_t hash_value(HashingStrategy strategy=HashingStrategy::kDefault) const
GenericBinopOp(V< Object > left, V< Object > right, V< FrameState > frame_state, V< Context > context, Kind kind, LazyDeoptOnThrow lazy_deopt_on_throw)
GenericUnopOp(V< Object > input, V< FrameState > frame_state, V< Context > context, Kind kind, LazyDeoptOnThrow lazy_deopt_on_throw)
base::Vector< const RegisterRepresentation > outputs_rep() const
GotoOp(Block *destination, bool is_backedge)
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
JSStackCheckOp(V< Context > context, OptionalV< FrameState > frame_state, Kind kind)
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
static JSStackCheckOp & New(Graph *graph, V< Context > context, OptionalV< FrameState > frame_state, Kind kind)
OptionalV< FrameState > frame_state() const
V8_INLINE auto Explode(Fn fn, Mapper &mapper) const
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const RegisterRepresentation > outputs_rep() const
LoadDataViewElementOp(OpIndex object, OpIndex storage, OpIndex index, OpIndex is_little_endian, ExternalArrayType element_type)
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
static constexpr Kind Aligned(BaseTaggedness base_is_tagged)
static constexpr Kind MaybeUnaligned(MemoryRepresentation rep)
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
V8_INLINE auto Explode(Fn fn, Mapper &mapper) const
static constexpr bool OffsetIsValid(int32_t offset, bool tagged_base)
void Validate(const Graph &graph) const
static LoadOp & New(Graph *graph, OpIndex base, OptionalOpIndex index, Kind kind, MemoryRepresentation loaded_rep, RegisterRepresentation result_rep, int32_t offset, uint8_t element_size_log2)
LoadOp(OpIndex base, OptionalOpIndex index, Kind kind, MemoryRepresentation loaded_rep, RegisterRepresentation result_rep, int32_t offset, uint8_t element_size_log2)
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
LoadTypedElementOp(OpIndex buffer, OpIndex base, OpIndex external, OpIndex index, ExternalArrayType array_type)
base::Vector< const RegisterRepresentation > outputs_rep() const
MaybeGrowFastElementsOp(V< Object > object, V< Object > elements, V< Word32 > index, V< Word32 > elements_length, V< FrameState > frame_state, GrowFastElementsMode mode, const FeedbackSource &feedback)
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
MemoryBarrierOp(AtomicMemoryOrder memory_order)
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const RegisterRepresentation > outputs_rep() const
NewArgumentsElementsOp(OpIndex arguments_count, CreateArgumentsType type, int formal_parameter_count)
NewArrayOp(OpIndex length, Kind kind, AllocationType allocation_type)
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const RegisterRepresentation > outputs_rep() const
NewConsStringOp(V< Word32 > length, V< String > first, V< String > second)
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
ObjectIsNumericValueOp(V< Object > input, NumericKind kind, FloatRepresentation input_rep)
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
ObjectIsOp(V< Object > input, Kind kind, InputAssumptions input_assumptions)
base::Vector< const RegisterRepresentation > outputs_rep() const
constexpr OpEffects CanWriteOffHeapMemory() const
Definition operations.h:714
bool operator==(OpEffects other) const
Definition operations.h:654
constexpr OpEffects CanLeaveCurrentFunction() const
Definition operations.h:778
OpEffects operator|(OpEffects other) const
Definition operations.h:656
constexpr OpEffects CanReadOffHeapMemory() const
Definition operations.h:706
constexpr OpEffects CanDependOnChecks() const
Definition operations.h:759
constexpr OpEffects CanDoRawHeapAccess() const
Definition operations.h:689
constexpr OpEffects CanWriteHeapMemory() const
Definition operations.h:728
constexpr OpEffects CanCallAnything() const
Definition operations.h:796
constexpr OpEffects CanCreateIdentity() const
Definition operations.h:790
constexpr OpEffects CanReadHeapMemory() const
Definition operations.h:697
constexpr OpEffects AssumesConsistentHeap() const
Definition operations.h:666
constexpr OpEffects CanReadImmutableMemory() const
Definition operations.h:752
constexpr OpEffects RequiredWhenUnused() const
Definition operations.h:804
constexpr OpEffects CanChangeControlFlow() const
Definition operations.h:765
OpEffects operator&(OpEffects other) const
Definition operations.h:659
constexpr OpEffects CanAllocateWithoutIdentity() const
Definition operations.h:677
constexpr OpEffects CanAllocate() const
Definition operations.h:684
constexpr OpEffects CanWriteMemory() const
Definition operations.h:741
bool IsSubsetOf(OpEffects other) const
Definition operations.h:662
constexpr OpEffects CanReadMemory() const
Definition operations.h:745
bool operator!=(OpEffects other) const
Definition operations.h:655
void PrintOptions(std::ostream &os) const
static Derived & New(Graph *graph, ShadowyOpIndexVectorWrapper inputs, Args... args)
static Derived & New(Graph *graph, size_t input_count, Args... args)
bool EqualsForGVN(const Base &other) const
void Validate(const Graph &graph) const
base::Vector< const RegisterRepresentation > outputs_rep() const
OperationT(ShadowyOpIndexVectorWrapper inputs)
bool operator==(const Base &other) const
void PrintInputs(std::ostream &os, const std::string &op_index_prefix) const
base::Vector< const OpIndex > inputs() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
size_t hash_value(HashingStrategy strategy=HashingStrategy::kDefault) const
size_t HashWithOptions(const Args &... args) const
V8_INLINE V< Any > input(size_t i) const
static constexpr std::optional< OpEffects > EffectsIfStatic()
static size_t StorageSlotCount(size_t input_count)
static void PrintOptionsHelper(std::ostream &os, const std::tuple< T... > &options, std::index_sequence< I... >)
V8_INLINE V< T > input(size_t i) const
base::SmallVector< OpIndex, N > Map(base::Vector< const OpIndex > indices)
Definition operations.h:939
const underlying_operation_t< Op > & Cast() const
Definition operations.h:985
V8_INLINE OpIndex input(size_t i) const
Definition operations.h:959
underlying_operation_t< Op > * TryCast()
Definition operations.h:995
Operation & operator=(const Operation &)=delete
V8_CLANG_NO_SANITIZE("memory") bool IsOpmask() const
Operation(Opcode opcode, size_t input_count)
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
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
OverflowCheckedBinopOp(V< Word > left, V< Word > right, Kind kind, WordRepresentation rep)
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
OverflowCheckedUnaryOp(V< Word > input, Kind kind, WordRepresentation rep)
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
ParameterOp(int32_t parameter_index, RegisterRepresentation rep, const char *debug_name="")
base::Vector< const RegisterRepresentation > outputs_rep() const
PendingLoopPhiOp(OpIndex first, RegisterRepresentation rep)
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
PhiOp(base::Vector< const OpIndex > inputs, RegisterRepresentation rep)
void Validate(const Graph &graph) const
V8_INLINE auto Explode(Fn fn, Mapper &mapper) const
base::Vector< const RegisterRepresentation > outputs_rep() const
ProjectionOp(V< Any > input, uint16_t index, RegisterRepresentation rep)
void Validate(const Graph &graph) const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const OpIndex > return_values() const
V8_INLINE auto Explode(Fn fn, Mapper &mapper) const
ReturnOp(V< Word32 > pop_count, base::Vector< const OpIndex > return_values, bool spill_caller_frame_slots)
static ReturnOp & New(Graph *graph, V< Word32 > pop_count, base::Vector< const OpIndex > return_values, bool spill_caller_frame_slots)
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const RegisterRepresentation > outputs_rep() const
SameValueOp(V< Object > left, V< Object > right, Mode mode)
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
void Validate(const Graph &graph) const
base::Vector< const RegisterRepresentation > outputs_rep() const
SelectOp(V< Word32 > cond, V< Any > vtrue, V< Any > vfalse, RegisterRepresentation rep, BranchHint hint, Implementation implem)
ShiftOp(V< Word > left, V< Word32 > right, Kind kind, WordRepresentation rep)
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
static bool AllowsWord64ToWord32Truncation(Kind kind)
StackPointerGreaterThanOp(V< WordPtr > stack_limit, StackCheckKind kind)
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const RegisterRepresentation > outputs_rep() const
StackSlotOp(int size, int alignment, bool is_tagged=false)
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
StaticAssertOp(V< Word32 > condition, const char *source)
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
StoreDataViewElementOp(OpIndex object, OpIndex storage, OpIndex index, OpIndex value, OpIndex is_little_endian, ExternalArrayType element_type)
base::Vector< const RegisterRepresentation > outputs_rep() const
StoreMessageOp(V< WordPtr > offset, V< Object > object)
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
V8_INLINE auto Explode(Fn fn, Mapper &mapper) const
base::Vector< const RegisterRepresentation > outputs_rep() const
IndirectPointerTag indirect_pointer_tag() const
static StoreOp & New(Graph *graph, OpIndex base, OptionalOpIndex index, OpIndex value, Kind kind, MemoryRepresentation stored_rep, WriteBarrierKind write_barrier, int32_t offset, uint8_t element_size_log2, bool maybe_initializing_or_transitioning, IndirectPointerTag maybe_indirect_pointer_tag=kIndirectPointerNullTag)
StoreOp(OpIndex base, OptionalOpIndex index, OpIndex value, Kind kind, MemoryRepresentation stored_rep, WriteBarrierKind write_barrier, int32_t offset, uint8_t element_size_log2, bool maybe_initializing_or_transitioning, IndirectPointerTag maybe_indirect_pointer_tag=kIndirectPointerNullTag)
void Validate(const Graph &graph) const
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
StoreTypedElementOp(OpIndex buffer, OpIndex base, OpIndex external, OpIndex index, OpIndex value, ExternalArrayType array_type)
StringAtOp(V< String > string, V< WordPtr > position, Kind kind)
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const RegisterRepresentation > outputs_rep() const
StringComparisonOp(V< String > left, V< String > right, Kind kind)
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const RegisterRepresentation > outputs_rep() const
StringConcatOp(V< Smi > length, V< String > left, V< String > right)
StringFromCodePointAtOp(V< String > string, V< WordPtr > index)
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const RegisterRepresentation > outputs_rep() const
StringIndexOfOp(V< String > string, V< String > search, V< Smi > position)
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
StringSubstringOp(V< String > string, V< Word32 > start, V< Word32 > end)
Case(int32_t value, Block *destination, BranchHint hint)
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const RegisterRepresentation > outputs_rep() const
SwitchOp(V< Word32 > input, base::Vector< Case > cases, Block *default_case, BranchHint default_hint)
static const TSCallDescriptor * Create(const CallDescriptor *descriptor, CanThrow can_throw, LazyDeoptOnThrow lazy_deopt_on_throw, Zone *graph_zone, const JSWasmCallParameters *js_wasm_call_parameters=nullptr)
base::Vector< const RegisterRepresentation > in_reps
TSCallDescriptor(const CallDescriptor *descriptor, base::Vector< const RegisterRepresentation > in_reps, base::Vector< const RegisterRepresentation > out_reps, CanThrow can_throw, LazyDeoptOnThrow lazy_deopt_on_throw, const JSWasmCallParameters *js_wasm_call_parameters)
base::Vector< const RegisterRepresentation > out_reps
const JSWasmCallParameters * js_wasm_call_parameters
TaggedBitcastOp(OpIndex input, RegisterRepresentation from, RegisterRepresentation to, Kind kind)
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const OpIndex > arguments() const
base::Vector< const RegisterRepresentation > outputs_rep() const
V8_INLINE auto Explode(Fn fn, Mapper &mapper) const
static TailCallOp & New(Graph *graph, OpIndex callee, base::Vector< const OpIndex > arguments, const TSCallDescriptor *descriptor)
TailCallOp(OpIndex callee, base::Vector< const OpIndex > arguments, const TSCallDescriptor *descriptor)
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
ToNumberOrNumericOp(V< Object > input, V< FrameState > frame_state, V< Context > context, Object::Conversion kind, LazyDeoptOnThrow lazy_deopt_on_throw)
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const RegisterRepresentation > outputs_rep() const
size_t hash_value(HashingStrategy strategy=HashingStrategy::kDefault) const
TransitionAndStoreArrayElementOp(V< JSArray > array, V< WordPtr > index, V< Any > value, Kind kind, MaybeIndirectHandle< Map > fast_map, MaybeIndirectHandle< Map > double_map)
bool operator==(const TransitionAndStoreArrayElementOp &other) const
base::Vector< const RegisterRepresentation > outputs_rep() const
TransitionElementsKindOp(OpIndex object, const ElementsTransition &transition)
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const RegisterRepresentation > outputs_rep() const
TransitionElementsKindOrCheckMapOp(V< HeapObject > object, V< Map > map, V< FrameState > frame_state, const ElementsTransitionWithMultipleSources &transition)
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
TruncateJSPrimitiveToUntaggedOp(V< JSPrimitive > input, UntaggedKind kind, InputAssumptions input_assumptions)
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
TruncateJSPrimitiveToUntaggedOrDeoptOp(V< JSPrimitive > input, V< FrameState > frame_state, UntaggedKind kind, InputRequirement input_requirement, const FeedbackSource &feedback)
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const RegisterRepresentation > outputs_rep() const
TryChangeOp(OpIndex input, Kind kind, FloatRepresentation from, WordRepresentation to)
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
V8_INLINE auto Explode(Fn fn, Mapper &mapper) const
base::Vector< const RegisterRepresentation > outputs_rep() const
TupleOp(base::Vector< const V< Any > > inputs)
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
TypedArrayLengthOp(V< JSTypedArray > typed_array, ElementsKind elements_kind)
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
Word32PairBinopOp(V< Word32 > left_low, V< Word32 > left_high, V< Word32 > right_low, V< Word32 > right_high, Kind kind)
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(const ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
WordBinopDeoptOnOverflowOp(V< Word > left, V< Word > right, V< FrameState > frame_state, Kind kind, WordRepresentation rep, FeedbackSource feedback, CheckForMinusZeroMode mode)
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
base::Vector< const RegisterRepresentation > outputs_rep() const
static bool AllowsWord64ToWord32Truncation(Kind kind)
WordBinopOp(V< Word > left, V< Word > right, Kind kind, WordRepresentation rep)
base::Vector< const RegisterRepresentation > outputs_rep() const
base::Vector< const MaybeRegisterRepresentation > inputs_rep(ZoneVector< MaybeRegisterRepresentation > &storage) const
WordUnaryOp(V< Word > input, Kind kind, WordRepresentation rep)
bool operator==(const ConstantOp::Storage &) const
Storage(IndirectHandle< HeapObject > constant)
#define V8_INLINE
Definition v8config.h:500
#define END_ALLOW_USE_DEPRECATED()
Definition v8config.h:634
#define V8_LIKELY(condition)
Definition v8config.h:661
#define START_ALLOW_USE_DEPRECATED()
Definition v8config.h:633
std::unique_ptr< ValueMirror > value
std::unique_ptr< ValueMirror > key
wasm::ValueType type