v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
graph-assembler.h
Go to the documentation of this file.
1// Copyright 2016 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_GRAPH_ASSEMBLER_H_
6#define V8_COMPILER_GRAPH_ASSEMBLER_H_
7
8#include <optional>
9#include <type_traits>
10
12#include "src/codegen/tnode.h"
13#include "src/common/globals.h"
16#include "src/compiler/node.h"
18#include "src/objects/hole.h"
19#include "src/objects/oddball.h"
20
21namespace v8 {
22namespace internal {
23
24class JSGraph;
25class TFGraph;
26
27namespace compiler {
28
29class Reducer;
30
31#define PURE_ASSEMBLER_MACH_UNOP_LIST(V) \
32 V(BitcastFloat32ToInt32) \
33 V(BitcastFloat64ToInt64) \
34 V(BitcastInt32ToFloat32) \
35 V(BitcastWord32ToWord64) \
36 V(BitcastInt64ToFloat64) \
37 V(ChangeFloat32ToFloat64) \
38 V(ChangeFloat64ToInt32) \
39 V(ChangeFloat64ToInt64) \
40 V(ChangeFloat64ToUint32) \
41 V(ChangeFloat64ToUint64) \
42 V(ChangeInt32ToFloat64) \
43 V(ChangeInt32ToInt64) \
44 V(ChangeInt64ToFloat64) \
45 V(ChangeUint32ToFloat64) \
46 V(ChangeUint32ToUint64) \
47 V(Float64Abs) \
48 V(Float64ExtractHighWord32) \
49 V(Float64ExtractLowWord32) \
50 V(Float64SilenceNaN) \
51 V(RoundFloat64ToInt32) \
52 V(RoundInt32ToFloat32) \
53 V(TruncateFloat64ToFloat32) \
54 V(TruncateFloat64ToWord32) \
55 V(TruncateInt64ToInt32) \
56 V(TryTruncateFloat64ToInt64) \
57 V(TryTruncateFloat64ToUint64) \
58 V(TryTruncateFloat64ToInt32) \
59 V(TryTruncateFloat64ToUint32) \
60 V(Word32ReverseBytes) \
61 V(Word64ReverseBytes)
62
63#define PURE_ASSEMBLER_MACH_BINOP_LIST(V, T) \
64 V(Float64Add) \
65 V(Float64Div) \
66 V(Float64Equal) \
67 V(Float64InsertHighWord32) \
68 V(Float64InsertLowWord32) \
69 V(Float64LessThan) \
70 V(Float64LessThanOrEqual) \
71 V(Float64Max) \
72 V(Float64Min) \
73 V(Float64Mod) \
74 V(Float64Sub) \
75 V(Int32Add) \
76 V(Int32LessThan) \
77 T(Int32LessThanOrEqual, BoolT, Int32T, Int32T) \
78 V(Int32Mul) \
79 V(Int32Sub) \
80 V(Int64Add) \
81 V(Int64Sub) \
82 V(IntAdd) \
83 V(IntLessThan) \
84 V(IntMul) \
85 V(IntSub) \
86 T(Uint32LessThan, BoolT, Uint32T, Uint32T) \
87 T(Uint32LessThanOrEqual, BoolT, Uint32T, Uint32T) \
88 T(Uint64LessThan, BoolT, Uint64T, Uint64T) \
89 T(Uint64LessThanOrEqual, BoolT, Uint64T, Uint64T) \
90 V(UintLessThan) \
91 T(Word32And, Word32T, Word32T, Word32T) \
92 T(Word32Equal, BoolT, Word32T, Word32T) \
93 T(Word32Or, Word32T, Word32T, Word32T) \
94 V(Word32Sar) \
95 V(Word32SarShiftOutZeros) \
96 V(Word32Shl) \
97 T(Word32Shr, Word32T, Word32T, Word32T) \
98 V(Word32Xor) \
99 V(Word64And) \
100 V(Word64Equal) \
101 V(Word64Or) \
102 V(Word64Sar) \
103 V(Word64SarShiftOutZeros) \
104 V(Word64Shl) \
105 V(Word64Shr) \
106 V(Word64Xor) \
107 V(WordAnd) \
108 V(WordEqual) \
109 V(WordOr) \
110 V(WordSar) \
111 V(WordSarShiftOutZeros) \
112 V(WordShl) \
113 V(WordShr) \
114 V(WordXor)
115
116#define CHECKED_ASSEMBLER_MACH_BINOP_LIST(V) \
117 V(Int32AddWithOverflow) \
118 V(Int64AddWithOverflow) \
119 V(Int32Div) \
120 V(Int32Mod) \
121 V(Int32MulWithOverflow) \
122 V(Int64MulWithOverflow) \
123 V(Int32SubWithOverflow) \
124 V(Int64SubWithOverflow) \
125 V(Int64Div) \
126 V(Int64Mod) \
127 V(Uint32Div) \
128 V(Uint32Mod) \
129 V(Uint64Div) \
130 V(Uint64Mod)
131
132#define JSGRAPH_SINGLETON_CONSTANT_LIST(V) \
133 V(AllocateInOldGenerationStub, InstructionStream) \
134 V(AllocateInYoungGenerationStub, InstructionStream) \
135 IF_WASM(V, WasmAllocateInYoungGenerationStub, InstructionStream) \
136 IF_WASM(V, WasmAllocateInOldGenerationStub, InstructionStream) \
137 V(BigIntMap, Map) \
138 V(BooleanMap, Map) \
139 V(EmptyString, String) \
140 V(ExternalObjectMap, Map) \
141 V(False, Boolean) \
142 V(FixedArrayMap, Map) \
143 V(FixedDoubleArrayMap, Map) \
144 V(WeakFixedArrayMap, Map) \
145 V(HeapNumberMap, Map) \
146 V(MinusOne, Number) \
147 V(NaN, Number) \
148 V(NoContext, Object) \
149 V(Null, Null) \
150 V(One, Number) \
151 V(TheHole, Hole) \
152 V(ToNumberBuiltin, InstructionStream) \
153 V(PlainPrimitiveToNumberBuiltin, InstructionStream) \
154 V(True, Boolean) \
155 V(Undefined, Undefined) \
156 V(Zero, Number)
157
158class GraphAssembler;
159
161
162namespace detail {
163constexpr size_t kGraphAssemblerLabelDynamicCount = ~0u;
164
165template <size_t VarCount>
167 template <typename T>
168 using Array = std::array<T, VarCount>;
169 static constexpr bool kIsDynamic = false;
170
172 return {};
173 }
174};
175template <>
177 // TODO(leszeks): We could allow other sizes of small vector here, by encoding
178 // the size in the negative VarCount.
179 template <typename T>
181 static constexpr bool kIsDynamic = true;
182
184 return Array<Node*>(reps.size());
185 }
186};
187} // namespace detail
188
189// Label with statically known count of incoming branches and phis.
190template <size_t VarCount>
191class GraphAssemblerLabel {
193 template <typename T>
194 using Array = typename Helper::template Array<T>;
195 static constexpr bool kIsDynamic = Helper::kIsDynamic;
196
197 public:
198 size_t Count() { return representations_.size(); }
199
200 Node* PhiAt(size_t index);
201
202 template <typename T>
203 TNode<T> PhiAt(size_t index) {
204 // TODO(jgruber): Investigate issues on ptr compression bots and enable.
205 // DCHECK(IsMachineRepresentationOf<T>(representations_[index]));
206 return TNode<T>::UncheckedCast(PhiAt(index));
207 }
208
209 bool IsUsed() const { return merged_count_ > 0; }
210
211 GraphAssemblerLabel(GraphAssemblerLabelType type, int loop_nesting_level,
213 : type_(type),
214 loop_nesting_level_(loop_nesting_level),
215 bindings_(Helper::InitNodeArray(reps)),
216 representations_(std::move(reps)) {}
217
219
220 private:
221 friend class GraphAssembler;
222
223 void SetBound() {
224 DCHECK(!IsBound());
225 is_bound_ = true;
226 }
227 bool IsBound() const { return is_bound_; }
228 bool IsDeferred() const {
230 }
231 bool IsLoop() const { return type_ == GraphAssemblerLabelType::kLoop; }
232
233 bool is_bound_ = false;
236 size_t merged_count_ = 0;
241};
242
245
246namespace detail {
247template <typename T, typename Enable, typename... Us>
249
250// If the Us are a template pack each assignable to T, use a static label.
251template <typename T, typename... Us>
253 T, std::enable_if_t<std::conjunction_v<std::is_assignable<T&, Us>...>>,
254 Us...> {
255 using Type = GraphAssemblerLabel<sizeof...(Us)>;
256};
257
258// If the single arg is a vector of U assignable to T, use a dynamic label.
259template <typename T, typename U>
261 T, std::enable_if_t<std::is_assignable_v<T&, U>>, base::SmallVector<U, 4>> {
263};
264
265template <typename... Vars>
267 typename GraphAssemblerLabelForXHelper<Node*, void, Vars...>::Type;
268
269template <typename... Reps>
272 Reps...>::Type;
273
274} // namespace detail
275
276using NodeChangedCallback = std::function<void(Node*)>;
278 public:
279 // Constructs a GraphAssembler. If {schedule} is not null, the graph assembler
280 // will maintain the schedule as it updates blocks.
282 MachineGraph* jsgraph, Zone* zone,
283 BranchSemantics default_branch_semantics,
284 std::optional<NodeChangedCallback> node_changed_callback = std::nullopt,
285 bool mark_loop_exits = false);
286 virtual ~GraphAssembler();
288
289 void Reset();
290 void InitializeEffectControl(Node* effect, Node* control);
291
292 // Create label.
293 template <typename... Reps>
295 GraphAssemblerLabelType type, Reps... reps) {
296 std::array<MachineRepresentation, sizeof...(Reps)> reps_array = {reps...};
298 type, loop_nesting_level_, std::move(reps_array));
299 }
303 return GraphAssemblerDynamicLabel(type, loop_nesting_level_,
304 std::move(reps));
305 }
306
307 // Convenience wrapper for creating non-deferred labels.
308 template <typename... Reps>
310 return MakeLabelFor(GraphAssemblerLabelType::kNonDeferred, reps...);
311 }
312
313 // Convenience wrapper for creating loop labels.
314 template <typename... Reps>
316 return MakeLabelFor(GraphAssemblerLabelType::kLoop, reps...);
317 }
318
319 // Convenience wrapper for creating deferred labels.
320 template <typename... Reps>
322 return MakeLabelFor(GraphAssemblerLabelType::kDeferred, reps...);
323 }
324
325 // Value creation.
326 Node* IntPtrConstant(intptr_t value);
327 TNode<UintPtrT> UintPtrConstant(uintptr_t value);
328 Node* Int32Constant(int32_t value);
329 TNode<Uint32T> Uint32Constant(uint32_t value);
330 Node* Int64Constant(int64_t value);
331 Node* Uint64Constant(uint64_t value);
332 Node* UniqueIntPtrConstant(intptr_t value);
333 Node* Float64Constant(double value);
334 Node* ExternalConstant(ExternalReference ref);
335 Node* IsolateField(IsolateFieldId id);
336
337 Node* Projection(int index, Node* value, Node* ctrl = nullptr);
338
339 Node* Parameter(int index);
340
341 Node* LoadFramePointer();
342
343 Node* LoadRootRegister();
344
345#if V8_ENABLE_WEBASSEMBLY
346 Node* LoadStackPointer();
347 Node* SetStackPointer(Node* sp);
348#endif
349
350 Node* LoadHeapNumberValue(Node* heap_number);
351
352#define PURE_UNOP_DECL(Name) Node* Name(Node* input);
354#undef PURE_UNOP_DECL
355
356#define BINOP_DECL(Name) Node* Name(Node* left, Node* right);
357#define BINOP_DECL_TNODE(Name, Result, Left, Right) \
358 TNode<Result> Name(SloppyTNode<Left> left, SloppyTNode<Right> right);
361#undef BINOP_DECL
362#undef BINOP_DECL_TNODE
363 TNode<BoolT> UintPtrLessThan(TNode<UintPtrT> left, TNode<UintPtrT> right);
364 TNode<BoolT> UintPtrLessThanOrEqual(TNode<UintPtrT> left,
365 TNode<UintPtrT> right);
366 TNode<UintPtrT> UintPtrAdd(TNode<UintPtrT> left, TNode<UintPtrT> right);
367 TNode<UintPtrT> UintPtrSub(TNode<UintPtrT> left, TNode<UintPtrT> right);
368 TNode<UintPtrT> UintPtrDiv(TNode<UintPtrT> left, TNode<UintPtrT> right);
369 TNode<UintPtrT> ChangeUint32ToUintPtr(SloppyTNode<Uint32T> value);
370
371#ifdef V8_MAP_PACKING
372 Node* PackMapWord(TNode<Map> map);
373 TNode<Map> UnpackMapWord(Node* map_word);
374#endif
375 TNode<Map> LoadMap(Node* object);
376
377 Node* DebugBreak();
378
379 Node* Unreachable();
380 // This special variant doesn't connect the Unreachable node to end, and does
381 // not reset current effect/control. Intended only for special use-cases like
382 // lowering DeadValue.
383 Node* UnreachableWithoutConnectToEnd();
384
385 Node* IntPtrEqual(Node* left, Node* right);
386 Node* TaggedEqual(Node* left, Node* right);
387
388 Node* SmiSub(Node* left, Node* right);
389 Node* SmiLessThan(Node* left, Node* right);
390
391 Node* Float64RoundDown(Node* value);
392 Node* Float64RoundTruncate(Node* value);
393 Node* TruncateFloat64ToInt64(Node* value, TruncateKind kind);
394
395 Node* BitcastWordToTagged(Node* value);
396 Node* BitcastWordToTaggedSigned(Node* value);
397 Node* BitcastTaggedToWord(Node* value);
398 Node* BitcastTaggedToWordForTagAndSmiBits(Node* value);
399 Node* BitcastMaybeObjectToWord(Node* value);
400
401 Node* TypeGuard(Type type, Node* value);
402 template <typename T>
404 return TNode<T>::UncheckedCast(TypeGuard(type, static_cast<Node*>(value)));
405 }
406 Node* Checkpoint(FrameState frame_state);
407
408 TNode<RawPtrT> StackSlot(int size, int alignment, bool is_tagged = false);
409
410 Node* AdaptLocalArgument(Node* argument);
411
412 Node* Store(StoreRepresentation rep, Node* object, Node* offset, Node* value);
413 Node* Store(StoreRepresentation rep, Node* object, int offset, Node* value);
414 Node* Load(MachineType type, Node* object, Node* offset);
415 Node* Load(MachineType type, Node* object, int offset);
416
417 Node* StoreUnaligned(MachineRepresentation rep, Node* object, Node* offset,
418 Node* value);
419 Node* LoadUnaligned(MachineType type, Node* object, Node* offset);
420
421 Node* ProtectedStore(MachineRepresentation rep, Node* object, Node* offset,
422 Node* value);
423 Node* ProtectedLoad(MachineType type, Node* object, Node* offset);
424 Node* LoadTrapOnNull(MachineType type, Node* object, Node* offset);
425 Node* StoreTrapOnNull(StoreRepresentation rep, Node* object, Node* offset,
426 Node* value);
427
428 Node* Retain(Node* buffer);
429 Node* IntPtrAdd(Node* a, Node* b);
430 Node* IntPtrSub(Node* a, Node* b);
431
432 Node* DeoptimizeIf(DeoptimizeReason reason, FeedbackSource const& feedback,
433 Node* condition, Node* frame_state);
434 Node* DeoptimizeIfNot(DeoptimizeReason reason, FeedbackSource const& feedback,
435 Node* condition, Node* frame_state);
436 TNode<Object> Call(const CallDescriptor* call_descriptor, int inputs_size,
437 Node** inputs);
438 TNode<Object> Call(const Operator* op, int inputs_size, Node** inputs);
439 template <typename... Args>
440 TNode<Object> Call(const CallDescriptor* call_descriptor, Node* first_arg,
441 Args... args);
442 template <typename... Args>
443 TNode<Object> Call(const Operator* op, Node* first_arg, Args... args);
444 void TailCall(const CallDescriptor* call_descriptor, int inputs_size,
445 Node** inputs);
446
447 // Basic control operations.
448 template <size_t VarCount>
450
451 template <typename... Vars>
453
454 // Branch hints are inferred from if_true/if_false deferred states.
455 void BranchWithCriticalSafetyCheck(Node* condition,
457 GraphAssemblerLabel<0u>* if_false);
458
459 // Branch hints are inferred from if_true/if_false deferred states.
460 template <typename... Vars>
461 void Branch(Node* condition,
464
465 template <typename... Vars>
466 void BranchWithHint(Node* condition,
469 BranchHint hint, Vars...);
470 template <typename... Vars>
471 void MachineBranch(TNode<Word32T> condition,
472 GraphAssemblerLabel<sizeof...(Vars)>* if_true,
473 GraphAssemblerLabel<sizeof...(Vars)>* if_false,
474 BranchHint hint, Vars...);
475 template <typename... Vars>
476 void JSBranch(TNode<Boolean> condition,
477 GraphAssemblerLabel<sizeof...(Vars)>* if_true,
478 GraphAssemblerLabel<sizeof...(Vars)>* if_false, BranchHint hint,
479 Vars...);
480
481 // Control helpers.
482
483 // {GotoIf(c, l, h)} is equivalent to {BranchWithHint(c, l, templ, h);
484 // Bind(templ)}.
485 template <typename... Vars>
486 void GotoIf(Node* condition,
488 BranchHint hint, Vars...);
489
490 // {GotoIfNot(c, l, h)} is equivalent to {BranchWithHint(c, templ, l, h);
491 // Bind(templ)}.
492 // The branch hint refers to the expected outcome of the provided condition,
493 // so {GotoIfNot(..., BranchHint::kTrue)} means "optimize for the case where
494 // the branch is *not* taken".
495 template <typename... Vars>
496 void GotoIfNot(Node* condition,
498 BranchHint hint, Vars...);
499
500 // {GotoIf(c, l)} is equivalent to {Branch(c, l, templ);Bind(templ)}.
501 template <typename... Vars>
502 void GotoIf(Node* condition,
504
505 // {GotoIfNot(c, l)} is equivalent to {Branch(c, templ, l);Bind(templ)}.
506 template <typename... Vars>
507 void GotoIfNot(Node* condition,
509
510 void RuntimeAbort(AbortReason reason);
511
512 bool HasActiveBlock() const {
513 // This is false if the current block has been terminated (e.g. by a Goto or
514 // Unreachable). In that case, a new label must be bound before we can
515 // continue emitting nodes.
516 return control() != nullptr;
517 }
518
519 // Updates current effect and control based on outputs of {node}.
521 if (node->op()->EffectOutputCount() > 0) {
522 effect_ = node;
523 }
524 if (node->op()->ControlOutputCount() > 0) {
525 control_ = node;
526 }
527 }
528
529 // Adds {node} to the current position and updates assembler's current effect
530 // and control.
531 Node* AddNode(Node* node);
532
533 template <typename T>
535 return TNode<T>::UncheckedCast(AddNode(node));
536 }
537
538 void ConnectUnreachableToEnd();
539
540 // Add an inline reducers such that nodes added to the graph will be run
541 // through the reducers and possibly further lowered. Each reducer should
542 // operate on independent node types since once a reducer changes a node we
543 // no longer run any other reducers on that node. The reducers should also
544 // only generate new nodes that wouldn't be further reduced, as new nodes
545 // generated by a reducer won't be passed through the reducers again.
546 void AddInlineReducer(Reducer* reducer) {
547 inline_reducers_.push_back(reducer);
548 }
549
550 Control control() const { return Control(control_); }
551 Effect effect() const { return Effect(effect_); }
552
553 Node* start() const { return graph()->start(); }
554
555 protected:
556 constexpr bool Is64() const { return kSystemPointerSize == 8; }
557
558 template <typename... Vars>
560 Vars... vars);
561
562 V8_INLINE Node* AddClonedNode(Node* node);
563
564 MachineGraph* mcgraph() const { return mcgraph_; }
565 TFGraph* graph() const { return mcgraph_->graph(); }
566 Zone* temp_zone() const { return temp_zone_; }
567 CommonOperatorBuilder* common() const { return mcgraph()->common(); }
568 MachineOperatorBuilder* machine() const { return mcgraph()->machine(); }
569
570 // Updates machinery for creating {LoopExit,LoopExitEffect,LoopExitValue}
571 // nodes on loop exits (which are necessary for loop peeling).
572 //
573 // All labels created while a LoopScope is live are considered to be inside
574 // the loop.
575 template <MachineRepresentation... Reps>
577 private:
578 // The internal scope is only here to increment the graph assembler's
579 // nesting level prior to `loop_header_label` creation below.
581 public:
583 : previous_loop_nesting_level_(gasm->loop_nesting_level_),
584 gasm_(gasm) {
585 gasm->loop_nesting_level_++;
586 }
587
589 gasm_->loop_nesting_level_--;
590 DCHECK_EQ(gasm_->loop_nesting_level_, previous_loop_nesting_level_);
591 }
592
593 private:
596 };
597
598 public:
599 explicit LoopScope(GraphAssembler* gasm)
600 : internal_scope_(gasm),
601 gasm_(gasm),
602 loop_header_label_(gasm->MakeLoopLabel(Reps...)) {
603 // This feature may only be used if it has been enabled.
604 DCHECK(gasm_->mark_loop_exits_);
605 gasm->loop_headers_.push_back(&loop_header_label_.control_);
606 DCHECK_EQ(static_cast<int>(gasm_->loop_headers_.size()),
607 gasm_->loop_nesting_level_);
608 }
609
611 DCHECK_EQ(static_cast<int>(gasm_->loop_headers_.size()),
612 gasm_->loop_nesting_level_);
613 gasm_->loop_headers_.pop_back();
614 }
615
617 return &loop_header_label_;
618 }
619
620 private:
623 GraphAssemblerLabel<sizeof...(Reps)> loop_header_label_;
624 };
625
626 // Upon destruction, restores effect and control to the state at construction.
628 public:
630 : gasm_(gasm), effect_(gasm->effect()), control_(gasm->control()) {}
631
633 gasm_->effect_ = effect_;
634 gasm_->control_ = control_;
635 }
636
637 private:
641 };
642
643 private:
645
646 template <typename... Vars>
647 void BranchImpl(BranchSemantics semantics, Node* condition,
648 GraphAssemblerLabel<sizeof...(Vars)>* if_true,
649 GraphAssemblerLabel<sizeof...(Vars)>* if_false,
650 BranchHint hint, Vars...);
651
657 // {node_changed_callback_} should be called when a node outside the
658 // subgraph created by the graph assembler changes.
659 std::optional<NodeChangedCallback> node_changed_callback_;
660
661 // Inline reducers enable reductions to be performed to nodes as they are
662 // added to the graph with the graph assembler.
665
666 // Track loop information in order to properly mark loop exits with
667 // {LoopExit,LoopExitEffect,LoopExitValue} nodes. The outermost level has
668 // a nesting level of 0. See also GraphAssembler::LoopScope.
669 int loop_nesting_level_ = 0;
671
672 // Feature configuration. As more features are added, this should be turned
673 // into a bitfield.
675};
676
677template <size_t VarCount>
679 DCHECK(IsBound());
680 DCHECK_LT(index, Count());
681 return bindings_[index];
682}
683
684template <typename... Vars>
687 using NodeArray = typename detail::GraphAssemblerLabelForVars<
688 Vars...>::template Array<Node*>;
689 RestoreEffectControlScope restore_effect_control_scope(this);
690
691 const int merged_count = static_cast<int>(label->merged_count_);
692
693 const size_t var_count = label->Count();
694 NodeArray var_array{vars...};
695
696 const bool is_loop_exit = label->loop_nesting_level_ != loop_nesting_level_;
697 if (is_loop_exit) {
698 // This feature may only be used if it has been enabled.
701 // Jumping from loops to loops not supported.
702 DCHECK(!label->IsLoop());
703 // Currently only the simple case of jumping one level is supported.
704 DCHECK_EQ(label->loop_nesting_level_, loop_nesting_level_ - 1);
705 DCHECK(!loop_headers_.empty());
707
708 // Mark this exit to enable loop peeling.
709 AddNode(graph()->NewNode(common()->LoopExit(), control(),
710 *loop_headers_.back()));
711 AddNode(graph()->NewNode(common()->LoopExitEffect(), effect(), control()));
712 for (size_t i = 0; i < var_count; i++) {
713 var_array[i] = AddNode(graph()->NewNode(
714 common()->LoopExitValue(MachineRepresentation::kTagged), var_array[i],
715 control()));
716 }
717 }
718
719 if (label->IsLoop()) {
720 if (merged_count == 0) {
721 DCHECK(!label->IsBound());
722 label->control_ =
723 graph()->NewNode(common()->Loop(2), control(), control());
724 label->effect_ = graph()->NewNode(common()->EffectPhi(2), effect(),
725 effect(), label->control_);
726 Node* terminate = graph()->NewNode(common()->Terminate(), label->effect_,
727 label->control_);
729 for (size_t i = 0; i < var_count; i++) {
730 label->bindings_[i] =
731 graph()->NewNode(common()->Phi(label->representations_[i], 2),
732 var_array[i], var_array[i], label->control_);
733 }
734 } else {
735 DCHECK(label->IsBound());
736 DCHECK_EQ(1, merged_count);
737 label->control_->ReplaceInput(1, control());
738 label->effect_->ReplaceInput(1, effect());
739 for (size_t i = 0; i < var_count; i++) {
740 label->bindings_[i]->ReplaceInput(1, var_array[i]);
741 CHECK(!NodeProperties::IsTyped(var_array[i])); // Unsupported.
742 }
743 }
744 } else {
745 DCHECK(!label->IsLoop());
746 DCHECK(!label->IsBound());
747 if (merged_count == 0) {
748 // Just set the control, effect and variables directly.
749 label->control_ = control();
750 label->effect_ = effect();
751 for (size_t i = 0; i < var_count; i++) {
752 label->bindings_[i] = var_array[i];
753 }
754 } else if (merged_count == 1) {
755 // Create merge, effect phi and a phi for each variable.
756 label->control_ =
757 graph()->NewNode(common()->Merge(2), label->control_, control());
758 label->effect_ = graph()->NewNode(common()->EffectPhi(2), label->effect_,
759 effect(), label->control_);
760 for (size_t i = 0; i < var_count; i++) {
761 label->bindings_[i] = graph()->NewNode(
762 common()->Phi(label->representations_[i], 2), label->bindings_[i],
763 var_array[i], label->control_);
764 }
765 } else {
766 // Append to the merge, effect phi and phis.
767 DCHECK_EQ(IrOpcode::kMerge, label->control_->opcode());
768 label->control_->AppendInput(graph()->zone(), control());
770 common()->Merge(merged_count + 1));
771
772 DCHECK_EQ(IrOpcode::kEffectPhi, label->effect_->opcode());
773 label->effect_->ReplaceInput(merged_count, effect());
774 label->effect_->AppendInput(graph()->zone(), label->control_);
776 common()->EffectPhi(merged_count + 1));
777
778 for (size_t i = 0; i < var_count; i++) {
779 DCHECK_EQ(IrOpcode::kPhi, label->bindings_[i]->opcode());
780 label->bindings_[i]->ReplaceInput(merged_count, var_array[i]);
781 label->bindings_[i]->AppendInput(graph()->zone(), label->control_);
783 label->bindings_[i],
784 common()->Phi(label->representations_[i], merged_count + 1));
785 if (NodeProperties::IsTyped(label->bindings_[i])) {
786 CHECK(NodeProperties::IsTyped(var_array[i]));
787 Type old_type = NodeProperties::GetType(label->bindings_[i]);
788 Type new_type = Type::Union(
789 old_type, NodeProperties::GetType(var_array[i]), graph()->zone());
790 NodeProperties::SetType(label->bindings_[i], new_type);
791 }
792 }
793 }
794 }
795 label->merged_count_++;
796}
797
798template <size_t VarCount>
802 DCHECK_LT(0, label->merged_count_);
803 DCHECK_EQ(label->loop_nesting_level_, loop_nesting_level_);
804
805 control_ = label->control_;
806 effect_ = label->effect_;
807
808 label->SetBound();
809
810 if (label->merged_count_ > 1 || label->IsLoop()) {
811 AddNode(label->control_);
812 AddNode(label->effect_);
813 for (size_t i = 0; i < label->Count(); i++) {
814 AddNode(label->bindings_[i]);
815 }
816 } else {
817 // If the basic block does not have a control node, insert a dummy
818 // Merge node, so that other passes have a control node to start from.
819 control_ = AddNode(graph()->NewNode(common()->Merge(1), control()));
820 }
821}
822
823template <typename... Vars>
826 detail::GraphAssemblerLabelForVars<Vars...>* if_false, Vars... vars) {
828 if (if_true->IsDeferred() != if_false->IsDeferred()) {
829 hint = if_false->IsDeferred() ? BranchHint::kTrue : BranchHint::kFalse;
830 }
831
832 BranchImpl(default_branch_semantics_, condition, if_true, if_false, hint,
833 vars...);
834}
835
836template <typename... Vars>
844
845template <typename... Vars>
847 TNode<Word32T> condition, GraphAssemblerLabel<sizeof...(Vars)>* if_true,
848 GraphAssemblerLabel<sizeof...(Vars)>* if_false, BranchHint hint,
849 Vars... vars) {
850 BranchImpl(BranchSemantics::kMachine, condition, if_true, if_false, hint,
851 vars...);
852}
853
854template <typename... Vars>
856 GraphAssemblerLabel<sizeof...(Vars)>* if_true,
857 GraphAssemblerLabel<sizeof...(Vars)>* if_false,
858 BranchHint hint, Vars... vars) {
859 BranchImpl(BranchSemantics::kJS, condition, if_true, if_false, hint, vars...);
860}
861
862template <typename... Vars>
864 GraphAssemblerLabel<sizeof...(Vars)>* if_true,
865 GraphAssemblerLabel<sizeof...(Vars)>* if_false,
866 BranchHint hint, Vars... vars) {
868
869 Node* branch =
870 graph()->NewNode(common()->Branch(hint, semantics), condition, control());
871
872 control_ = graph()->NewNode(common()->IfTrue(), branch);
873 MergeState(if_true, vars...);
874
875 control_ = graph()->NewNode(common()->IfFalse(), branch);
876 MergeState(if_false, vars...);
877
878 control_ = nullptr;
879 effect_ = nullptr;
880}
881
882template <typename... Vars>
884 Vars... vars) {
887 MergeState(label, vars...);
888
889 control_ = nullptr;
890 effect_ = nullptr;
891}
892
893template <typename... Vars>
896 BranchHint hint, Vars... vars) {
897 Node* branch = graph()->NewNode(
899
900 control_ = graph()->NewNode(common()->IfTrue(), branch);
901 MergeState(label, vars...);
902
903 control_ = AddNode(graph()->NewNode(common()->IfFalse(), branch));
904}
905
906template <typename... Vars>
909 BranchHint hint, Vars... vars) {
910 Node* branch = graph()->NewNode(
912
913 control_ = graph()->NewNode(common()->IfFalse(), branch);
914 MergeState(label, vars...);
915
916 control_ = AddNode(graph()->NewNode(common()->IfTrue(), branch));
917}
918
919template <typename... Vars>
922 Vars... vars) {
923 BranchHint hint =
925 return GotoIf(condition, label, hint, vars...);
926}
927
928template <typename... Vars>
931 Vars... vars) {
932 BranchHint hint = label->IsDeferred() ? BranchHint::kTrue : BranchHint::kNone;
933 return GotoIfNot(condition, label, hint, vars...);
934}
935
936template <typename... Args>
938 Node* first_arg, Args... args) {
939 const Operator* op = common()->Call(call_descriptor);
940 return Call(op, first_arg, args...);
941}
942
943template <typename... Args>
945 Args... args) {
946 Node* args_array[] = {first_arg, args..., effect(), control()};
947 int size = static_cast<int>(1 + sizeof...(args)) + op->EffectInputCount() +
948 op->ControlInputCount();
949 return Call(op, size, args_array);
950}
951
953 public:
954 // Constructs a JSGraphAssembler. If {schedule} is not null, the graph
955 // assembler will maintain the schedule as it updates blocks.
958 BranchSemantics branch_semantics,
959 std::optional<NodeChangedCallback> node_changed_callback = std::nullopt,
960 bool mark_loop_exits = false)
961 : GraphAssembler(jsgraph, zone, branch_semantics, node_changed_callback,
962 mark_loop_exits),
964 jsgraph_(jsgraph),
965 outermost_catch_scope_(CatchScope::Outermost(zone)),
966 catch_scope_(&outermost_catch_scope_) {
967 outermost_catch_scope_.set_gasm(this);
968 }
969
970 TNode<Smi> SmiConstant(int32_t value);
971 TNode<HeapObject> HeapConstant(Handle<HeapObject> object);
972 TNode<Object> Constant(ObjectRef ref);
973 TNode<Number> NumberConstant(double value);
974 Node* CEntryStubConstant(int result_size);
975
976#define SINGLETON_CONST_DECL(Name, Type) TNode<Type> Name##Constant();
978#undef SINGLETON_CONST_DECL
979
980#define SINGLETON_CONST_TEST_DECL(Name, ...) \
981 TNode<Boolean> Is##Name(TNode<Object> value);
983#undef SINGLETON_CONST_TEST_DECL
984
985 Node* Allocate(AllocationType allocation, Node* size);
986 TNode<Map> LoadMap(TNode<HeapObject> object);
987 Node* LoadField(FieldAccess const&, Node* object);
988 template <typename T>
990 // TODO(jgruber): Investigate issues on ptr compression bots and enable.
991 // DCHECK(IsMachineRepresentationOf<T>(
992 // access.machine_type.representation()));
993 return TNode<T>::UncheckedCast(LoadField(access, object));
994 }
995 TNode<Uint32T> LoadElementsKind(TNode<Map> map);
996 Node* LoadElement(ElementAccess const&, Node* object, Node* index);
997 template <typename T>
999 TNode<Number> index) {
1000 // TODO(jgruber): Investigate issues on ptr compression bots and enable.
1001 // DCHECK(IsMachineRepresentationOf<T>(
1002 // access.machine_type.representation()));
1003 return TNode<T>::UncheckedCast(LoadElement(access, object, index));
1004 }
1005 Node* StoreField(FieldAccess const&, Node* object, Node* value);
1006 Node* StoreElement(ElementAccess const&, Node* object, Node* index,
1007 Node* value);
1008 Node* ClearPendingMessage();
1009
1010 void TransitionAndStoreElement(MapRef double_map, MapRef fast_map,
1011 TNode<HeapObject> object, TNode<Number> index,
1012 TNode<Object> value);
1013 TNode<Number> StringLength(TNode<String> string);
1014 TNode<Boolean> ReferenceEqual(TNode<Object> lhs, TNode<Object> rhs);
1015 TNode<Number> PlainPrimitiveToNumber(TNode<Object> value);
1016 TNode<Number> NumberMin(TNode<Number> lhs, TNode<Number> rhs);
1017 TNode<Number> NumberMax(TNode<Number> lhs, TNode<Number> rhs);
1018 TNode<Boolean> NumberEqual(TNode<Number> lhs, TNode<Number> rhs);
1019 TNode<Boolean> NumberLessThan(TNode<Number> lhs, TNode<Number> rhs);
1020 TNode<Boolean> NumberLessThanOrEqual(TNode<Number> lhs, TNode<Number> rhs);
1021 TNode<Number> NumberAdd(TNode<Number> lhs, TNode<Number> rhs);
1022 TNode<Number> NumberSubtract(TNode<Number> lhs, TNode<Number> rhs);
1023 TNode<Number> NumberShiftRightLogical(TNode<Number> lhs, TNode<Number> rhs);
1024 TNode<Number> NumberBitwiseAnd(TNode<Number> lhs, TNode<Number> rhs);
1025 TNode<Number> NumberBitwiseOr(TNode<Number> lhs, TNode<Number> rhs);
1028 TNode<String> StringSubstring(TNode<String> string, TNode<Number> from,
1029 TNode<Number> to);
1030 TNode<Boolean> ObjectIsCallable(TNode<Object> value);
1031 TNode<Boolean> ObjectIsSmi(TNode<Object> value);
1032 TNode<Boolean> ObjectIsUndetectable(TNode<Object> value);
1033 Node* BooleanNot(Node* cond);
1034 Node* CheckSmi(Node* value, const FeedbackSource& feedback = {});
1035 Node* CheckNumber(Node* value, const FeedbackSource& feedback = {});
1036 Node* CheckNumberFitsInt32(Node* value, const FeedbackSource& feedback = {});
1037 Node* CheckIf(Node* cond, DeoptimizeReason reason,
1038 const FeedbackSource& feedback = {});
1039 Node* Assert(Node* cond, const char* condition_string = "",
1040 const char* file = "", int line = -1);
1041 void Assert(TNode<Word32T> cond, const char* condition_string = "",
1042 const char* file = "", int line = -1);
1043 TNode<Boolean> NumberIsFloat64Hole(TNode<Number> value);
1044 TNode<Boolean> ToBoolean(TNode<Object> value);
1045 TNode<Object> ConvertTaggedHoleToUndefined(TNode<Object> value);
1046 TNode<FixedArrayBase> MaybeGrowFastElements(ElementsKind kind,
1047 const FeedbackSource& feedback,
1048 TNode<JSArray> array,
1049 TNode<FixedArrayBase> elements,
1050 TNode<Number> new_length,
1051 TNode<Number> old_length);
1052 Node* StringCharCodeAt(TNode<String> string, TNode<Number> position);
1053 TNode<String> StringFromSingleCharCode(TNode<Number> code);
1054 TNode<Object> DoubleArrayMax(TNode<JSArray> array);
1055 TNode<Object> DoubleArrayMin(TNode<JSArray> array);
1056 // Computes the byte length for a given {array_buffer_view}. If the set of
1057 // possible ElementsKinds is known statically pass as
1058 // {elements_kinds_candidates} to allow the assembler to generate more
1059 // efficient code. Pass an empty {elements_kinds_candidates} to generate code
1060 // that is generic enough to handle all ElementsKinds.
1061 TNode<Number> ArrayBufferViewByteLength(
1062 TNode<JSArrayBufferView> array_buffer_view, InstanceType instance_type,
1063 std::set<ElementsKind> elements_kinds_candidates, TNode<Context> context);
1064 // Load just the detached bit on a TypedArray or DataView. For the full
1065 // detached and out-of-bounds check on TypedArrays, please use
1066 // CheckIfTypedArrayWasDetachedOrOutOfBounds.
1067 TNode<Word32T> ArrayBufferDetachedBit(TNode<HeapObject> buffer);
1068 TNode<Word32T> ArrayBufferViewDetachedBit(
1069 TNode<JSArrayBufferView> array_buffer_view);
1070 // Computes the length for a given {typed_array}. If the set of possible
1071 // ElementsKinds is known statically pass as {elements_kinds_candidates} to
1072 // allow the assembler to generate more efficient code. Pass an empty
1073 // {elements_kinds_candidates} to generate code that is generic enough to
1074 // handle all ElementsKinds.
1075 TNode<Number> TypedArrayLength(
1076 TNode<JSTypedArray> typed_array,
1077 std::set<ElementsKind> elements_kinds_candidates, TNode<Context> context);
1078 // Performs the full detached check. This includes fixed-length RABs whos
1079 // underlying buffer has been shrunk OOB.
1080 void CheckIfTypedArrayWasDetachedOrOutOfBounds(
1081 TNode<JSTypedArray> typed_array,
1082 std::set<ElementsKind> elements_kinds_candidates,
1083 const FeedbackSource& feedback);
1084 TNode<Uint32T> LookupByteShiftForElementsKind(TNode<Uint32T> elements_kind);
1085 TNode<Uint32T> LookupByteSizeForElementsKind(TNode<Uint32T> elements_kind);
1086
1087 TNode<Object> JSCallRuntime1(
1088 Runtime::FunctionId function_id, TNode<Object> arg0,
1089 TNode<Context> context, std::optional<FrameState> frame_state,
1090 Operator::Properties properties = Operator::kNoProperties);
1091 TNode<Object> JSCallRuntime2(Runtime::FunctionId function_id,
1092 TNode<Object> arg0, TNode<Object> arg1,
1093 TNode<Context> context, FrameState frame_state);
1094 Node* Chained(const Operator* op, Node* input);
1095
1096 JSHeapBroker* broker() const { return broker_; }
1097 JSGraph* jsgraph() const { return jsgraph_; }
1098 Isolate* isolate() const { return jsgraph()->isolate(); }
1100 return jsgraph()->simplified();
1101 }
1102 JSOperatorBuilder* javascript() const { return jsgraph()->javascript(); }
1103
1104 template <typename T, typename U>
1106 DCHECK_EQ(use_info.type_check(), TypeCheckKind::kNone);
1107 return AddNode<T>(
1108 graph()->NewNode(common()->EnterMachineGraph(use_info), input));
1109 }
1110
1111 template <typename T, typename U>
1113 MachineRepresentation output_representation,
1114 Type output_type) {
1115 return AddNode<T>(graph()->NewNode(
1116 common()->ExitMachineGraph(output_representation, output_type), input));
1117 }
1118
1119 // A catch scope represents a single catch handler. The handler can be
1120 // custom catch logic within the reduction itself; or a catch handler in the
1121 // outside graph into which the reduction will be integrated (in this case
1122 // the scope is called 'outermost').
1124 private:
1125 // Only used to partially construct the outermost scope.
1126 explicit CatchScope(Zone* zone) : if_exception_nodes_(zone) {}
1127
1128 // For all inner scopes.
1130 : gasm_(gasm),
1131 parent_(gasm->catch_scope_),
1132 has_handler_(true),
1133 if_exception_nodes_(zone) {
1135 gasm_->catch_scope_ = this;
1136 }
1137
1138 public:
1139 ~CatchScope() { gasm_->catch_scope_ = parent_; }
1140
1141 static CatchScope Outermost(Zone* zone) { return CatchScope{zone}; }
1142 static CatchScope Inner(Zone* zone, JSGraphAssembler* gasm) {
1143 return {zone, gasm};
1144 }
1145
1146 bool has_handler() const { return has_handler_; }
1147 bool is_outermost() const { return parent_ == nullptr; }
1148 CatchScope* parent() const { return parent_; }
1149
1150 // Should only be used to initialize the outermost scope (inner scopes
1151 // always have a handler and are passed the gasm pointer at construction).
1152 void set_has_handler(bool v) {
1153 DCHECK(is_outermost());
1154 has_handler_ = v;
1155 }
1157 DCHECK(is_outermost());
1158 DCHECK_NOT_NULL(v);
1159 gasm_ = v;
1160 }
1161
1163 return !if_exception_nodes_.empty();
1164 }
1165
1166 void RegisterIfExceptionNode(Node* if_exception) {
1167 DCHECK(has_handler());
1168 if_exception_nodes_.push_back(if_exception);
1169 }
1170
1172 Control* control_out) {
1173 DCHECK(has_handler());
1174 DCHECK(has_exceptional_control_flow());
1175
1176 const int size = static_cast<int>(if_exception_nodes_.size());
1177
1178 if (size == 1) {
1179 // No merge needed.
1180 Node* e = if_exception_nodes_.at(0);
1182 *effect_out = Effect(e);
1183 *control_out = Control(e);
1184 } else {
1185 DCHECK_GT(size, 1);
1186
1187 Node* merge = gasm_->graph()->NewNode(gasm_->common()->Merge(size),
1188 size, if_exception_nodes_.data());
1189
1190 // These phis additionally take {merge} as an input. Temporarily add
1191 // it to the list.
1192 if_exception_nodes_.push_back(merge);
1193 const int size_with_merge =
1194 static_cast<int>(if_exception_nodes_.size());
1195
1196 Node* ephi = gasm_->graph()->NewNode(gasm_->common()->EffectPhi(size),
1197 size_with_merge,
1198 if_exception_nodes_.data());
1199 Node* phi = gasm_->graph()->NewNode(
1200 gasm_->common()->Phi(MachineRepresentation::kTagged, size),
1201 size_with_merge, if_exception_nodes_.data());
1202 if_exception_nodes_.pop_back();
1203
1205 *effect_out = Effect(ephi);
1206 *control_out = Control(merge);
1207 }
1208 }
1209
1210 private:
1212 CatchScope* const parent_ = nullptr;
1213 bool has_handler_ = false;
1215 };
1216
1217 CatchScope* catch_scope() const { return catch_scope_; }
1218 Node* outermost_handler() const { return outermost_handler_; }
1219
1220 using NodeGenerator0 = std::function<TNode<Object>()>;
1221 // TODO(jgruber): Currently, it's the responsibility of the developer to note
1222 // which operations may throw and appropriately wrap these in a call to
1223 // MayThrow (see e.g. JSCall3 and CallRuntime2). A more methodical approach
1224 // would be good.
1226 TNode<Object> result = body();
1227
1228 if (catch_scope()->has_handler()) {
1229 // The IfException node is later merged into the outer graph.
1230 // Note: AddNode is intentionally not called since effect and control
1231 // should not be updated.
1232 Node* if_exception =
1233 graph()->NewNode(common()->IfException(), effect(), control());
1234 catch_scope()->RegisterIfExceptionNode(if_exception);
1235
1236 // Control resumes here.
1237 AddNode(graph()->NewNode(common()->IfSuccess(), control()));
1238 }
1239
1240 return result;
1241 }
1242
1243 using VoidGenerator0 = std::function<void()>;
1244 // TODO(jgruber): Currently IfBuilder0 and IfBuilder1 are implemented as
1245 // separate classes. If, in the future, we encounter additional use cases that
1246 // return more than 1 value, we should merge these back into a single variadic
1247 // implementation.
1248 class IfBuilder0 final {
1249 public:
1250 IfBuilder0(JSGraphAssembler* gasm, TNode<Boolean> cond, bool negate_cond)
1251 : gasm_(gasm),
1252 cond_(cond),
1253 negate_cond_(negate_cond),
1254 initial_effect_(gasm->effect()),
1255 initial_control_(gasm->control()) {}
1256
1258 DCHECK_EQ(hint_, BranchHint::kNone);
1259 hint_ = BranchHint::kTrue;
1260 return *this;
1261 }
1263 DCHECK_EQ(hint_, BranchHint::kNone);
1264 hint_ = BranchHint::kFalse;
1265 return *this;
1266 }
1267
1269 then_body_ = body;
1270 return *this;
1271 }
1273 else_body_ = body;
1274 return *this;
1275 }
1276
1278 // Ensure correct usage: effect/control must not have been modified while
1279 // the IfBuilder0 instance is alive.
1280 DCHECK_EQ(gasm_->effect(), initial_effect_);
1281 DCHECK_EQ(gasm_->control(), initial_control_);
1282
1283 // Unlike IfBuilder1, this supports an empty then or else body. This is
1284 // possible since the merge does not take any value inputs.
1285 DCHECK(then_body_ || else_body_);
1286
1287 if (negate_cond_) std::swap(then_body_, else_body_);
1288
1289 auto if_true = (hint_ == BranchHint::kFalse) ? gasm_->MakeDeferredLabel()
1290 : gasm_->MakeLabel();
1291 auto if_false = (hint_ == BranchHint::kTrue) ? gasm_->MakeDeferredLabel()
1292 : gasm_->MakeLabel();
1293 auto merge = gasm_->MakeLabel();
1294 gasm_->Branch(cond_, &if_true, &if_false);
1295
1296 gasm_->Bind(&if_true);
1297 if (then_body_) then_body_();
1298 if (gasm_->HasActiveBlock()) gasm_->Goto(&merge);
1299
1300 gasm_->Bind(&if_false);
1301 if (else_body_) else_body_();
1302 if (gasm_->HasActiveBlock()) gasm_->Goto(&merge);
1303
1304 gasm_->Bind(&merge);
1305 }
1306
1307 IfBuilder0(const IfBuilder0&) = delete;
1309
1310 private:
1313 const bool negate_cond_;
1316 BranchHint hint_ = BranchHint::kNone;
1319 };
1320
1321 IfBuilder0 If(TNode<Boolean> cond) { return {this, cond, false}; }
1322 IfBuilder0 IfNot(TNode<Boolean> cond) { return {this, cond, true}; }
1323
1324 template <typename T, typename Cond>
1326 using If1BodyFunction = std::function<TNode<T>()>;
1327
1328 public:
1329 IfBuilder1(JSGraphAssembler* gasm, TNode<Cond> cond, bool negate_cond)
1330 : gasm_(gasm), cond_(cond), negate_cond_(negate_cond) {}
1331
1333 DCHECK_EQ(hint_, BranchHint::kNone);
1334 hint_ = BranchHint::kTrue;
1335 return *this;
1336 }
1337
1339 DCHECK_EQ(hint_, BranchHint::kNone);
1340 hint_ = BranchHint::kFalse;
1341 return *this;
1342 }
1343
1345 then_body_ = body;
1346 return *this;
1347 }
1349 else_body_ = body;
1350 return *this;
1351 }
1352
1354 DCHECK(then_body_);
1355 DCHECK(else_body_);
1356
1357 if (negate_cond_) std::swap(then_body_, else_body_);
1358
1359 auto if_true = (hint_ == BranchHint::kFalse) ? gasm_->MakeDeferredLabel()
1360 : gasm_->MakeLabel();
1361 auto if_false = (hint_ == BranchHint::kTrue) ? gasm_->MakeDeferredLabel()
1362 : gasm_->MakeLabel();
1363 auto merge = gasm_->MakeLabel(PhiMachineRepresentationOf<T>);
1364 if constexpr (std::is_same_v<Cond, Word32T>) {
1365 gasm_->MachineBranch(cond_, &if_true, &if_false, hint_);
1366 } else {
1367 static_assert(std::is_same_v<Cond, Boolean>);
1368 if (hint_ != BranchHint::kNone) {
1369 gasm_->BranchWithHint(cond_, &if_true, &if_false, hint_);
1370 } else {
1371 gasm_->Branch(cond_, &if_true, &if_false);
1372 }
1373 }
1374
1375 gasm_->Bind(&if_true);
1376 TNode<T> then_result = then_body_();
1377 if (gasm_->HasActiveBlock()) gasm_->Goto(&merge, then_result);
1378
1379 gasm_->Bind(&if_false);
1380 TNode<T> else_result = else_body_();
1381 if (gasm_->HasActiveBlock()) {
1382 gasm_->Goto(&merge, else_result);
1383 }
1384
1385 gasm_->Bind(&merge);
1386 return merge.template PhiAt<T>(0);
1387 }
1388
1389 private:
1390 static constexpr MachineRepresentation kPhiRepresentation =
1391 MachineRepresentation::kTagged;
1392
1395 const bool negate_cond_;
1396 BranchHint hint_ = BranchHint::kNone;
1399 };
1400
1401 template <typename T>
1403 return {this, cond, false};
1404 }
1405 template <typename T>
1407 return {this, cond, true};
1408 }
1409 template <typename T>
1411 return {this, cond, false};
1412 }
1413 template <typename T>
1415 TNode<T> false_value,
1416 BranchHint hint = BranchHint::kNone) {
1417 return TNode<T>::UncheckedCast(AddNode(
1418 graph()->NewNode(common()->Select(T::kMachineRepresentation, hint,
1419 BranchSemantics::kMachine),
1420 cond, true_value, false_value)));
1421 }
1422
1423 protected:
1424 Operator const* PlainPrimitiveToNumberOperator();
1425
1426 private:
1430
1431 protected:
1435 friend class CatchScope;
1436};
1437
1438} // namespace compiler
1439} // namespace internal
1440} // namespace v8
1441
1442#endif // V8_COMPILER_GRAPH_ASSEMBLER_H_
TFGraph * graph
JSGraph * jsgraph
#define T
#define Assert(condition)
Builtins::Kind kind
Definition builtins.cc:40
size_t size() const
Isolate * isolate() const
Definition factory.h:1281
static TNode UncheckedCast(compiler::Node *node)
Definition tnode.h:413
const Operator * Call(const CallDescriptor *call_descriptor)
GraphAssemblerLabel(GraphAssemblerLabelType type, int loop_nesting_level, Array< MachineRepresentation > reps)
typename Helper::template Array< T > Array
const Array< MachineRepresentation > representations_
GraphAssemblerLabel< sizeof...(Reps)> * loop_header_label()
detail::GraphAssemblerLabelForReps< Reps... > MakeLabel(Reps... reps)
void MergeState(detail::GraphAssemblerLabelForVars< Vars... > *label, Vars... vars)
virtual SimplifiedOperatorBuilder * simplified()
GraphAssemblerDynamicLabel MakeLabelFor(GraphAssemblerLabelType type, base::SmallVector< MachineRepresentation, 4 > reps)
void BranchWithHint(Node *condition, detail::GraphAssemblerLabelForVars< Vars... > *if_true, detail::GraphAssemblerLabelForVars< Vars... > *if_false, BranchHint hint, Vars...)
void BranchImpl(BranchSemantics semantics, Node *condition, GraphAssemblerLabel< sizeof...(Vars)> *if_true, GraphAssemblerLabel< sizeof...(Vars)> *if_false, BranchHint hint, Vars...)
V8_INLINE void UpdateEffectControlWith(Node *node)
std::optional< NodeChangedCallback > node_changed_callback_
MachineOperatorBuilder * machine() const
detail::GraphAssemblerLabelForReps< Reps... > MakeLoopLabel(Reps... reps)
TNode< Object > Call(const CallDescriptor *call_descriptor, int inputs_size, Node **inputs)
detail::GraphAssemblerLabelForReps< Reps... > MakeDeferredLabel(Reps... reps)
void GotoIf(Node *condition, detail::GraphAssemblerLabelForVars< Vars... > *label, BranchHint hint, Vars...)
void MachineBranch(TNode< Word32T > condition, GraphAssemblerLabel< sizeof...(Vars)> *if_true, GraphAssemblerLabel< sizeof...(Vars)> *if_false, BranchHint hint, Vars...)
TNode< T > TypeGuard(Type type, TNode< T > value)
CommonOperatorBuilder * common() const
void JSBranch(TNode< Boolean > condition, GraphAssemblerLabel< sizeof...(Vars)> *if_true, GraphAssemblerLabel< sizeof...(Vars)> *if_false, BranchHint hint, Vars...)
void Bind(GraphAssemblerLabel< VarCount > *label)
void GotoIfNot(Node *condition, detail::GraphAssemblerLabelForVars< Vars... > *label, BranchHint hint, Vars...)
void Branch(Node *condition, detail::GraphAssemblerLabelForVars< Vars... > *if_true, detail::GraphAssemblerLabelForVars< Vars... > *if_false, Vars...)
void Goto(detail::GraphAssemblerLabelForVars< Vars... > *label, Vars...)
detail::GraphAssemblerLabelForReps< Reps... > MakeLabelFor(GraphAssemblerLabelType type, Reps... reps)
void MergeExceptionalPaths(TNode< Object > *exception_out, Effect *effect_out, Control *control_out)
static CatchScope Inner(Zone *zone, JSGraphAssembler *gasm)
IfBuilder0 & Else(const VoidGenerator0 &body)
IfBuilder0 & operator=(const IfBuilder0 &)=delete
IfBuilder0(JSGraphAssembler *gasm, TNode< Boolean > cond, bool negate_cond)
IfBuilder0 & Then(const VoidGenerator0 &body)
V8_WARN_UNUSED_RESULT IfBuilder1 & ExpectFalse()
V8_WARN_UNUSED_RESULT IfBuilder1 & ExpectTrue()
IfBuilder1(JSGraphAssembler *gasm, TNode< Cond > cond, bool negate_cond)
V8_WARN_UNUSED_RESULT IfBuilder1 & Else(const If1BodyFunction &body)
V8_WARN_UNUSED_RESULT IfBuilder1 & Then(const If1BodyFunction &body)
std::function< TNode< Object >()> NodeGenerator0
JSGraphAssembler(JSHeapBroker *broker, JSGraph *jsgraph, Zone *zone, BranchSemantics branch_semantics, std::optional< NodeChangedCallback > node_changed_callback=std::nullopt, bool mark_loop_exits=false)
TNode< Object > MayThrow(const NodeGenerator0 &body)
TNode< T > LoadField(FieldAccess const &access, TNode< HeapObject > object)
TNode< T > ExitMachineGraph(TNode< U > input, MachineRepresentation output_representation, Type output_type)
SetOncePointer< Operator const > to_number_operator_
TNode< T > LoadElement(ElementAccess const &access, TNode< HeapObject > object, TNode< Number > index)
IfBuilder1< T, Word32T > MachineSelectIf(TNode< Word32T > cond)
IfBuilder1< T, Boolean > SelectIf(TNode< Boolean > cond)
IfBuilder1< T, Boolean > SelectIfNot(TNode< Boolean > cond)
TNode< Number > NumberDivide(TNode< Number > lhs, TNode< Number > rhs)
TNode< Number > NumberFloor(TNode< Number > value)
TNode< T > MachineSelect(TNode< Word32T > cond, TNode< T > true_value, TNode< T > false_value, BranchHint hint=BranchHint::kNone)
TNode< T > EnterMachineGraph(TNode< U > input, UseInfo use_info)
IfBuilder0 IfNot(TNode< Boolean > cond)
IfBuilder0 If(TNode< Boolean > cond)
SimplifiedOperatorBuilder * simplified() override
static void ChangeOp(Node *node, const Operator *new_op)
static Type GetType(const Node *node)
static bool IsTyped(const Node *node)
static void MergeControlToEnd(TFGraph *graph, CommonOperatorBuilder *common, Node *node)
static void SetType(Node *node, Type type)
Node * NewNode(const Operator *op, int input_count, Node *const *inputs, bool incomplete=false)
static Type Union(Type type1, Type type2, Zone *zone)
TypeCheckKind type_check() const
Definition use-info.h:355
JSHeapBroker *const broker_
base::Vector< const DirectHandle< Object > > args
Definition execution.cc:74
MaybeDirectHandle< Object > * exception_out
Definition execution.cc:80
Label label
#define PURE_UNOP_DECL(Name)
#define BINOP_DECL_TNODE(Name, Result, Left, Right)
#define BINOP_DECL(Name)
#define SINGLETON_CONST_TEST_DECL(Name,...)
#define PURE_ASSEMBLER_MACH_BINOP_LIST(V, T)
#define CHECKED_ASSEMBLER_MACH_BINOP_LIST(V)
#define SINGLETON_CONST_DECL(Name, Type)
#define PURE_ASSEMBLER_MACH_UNOP_LIST(V)
#define JSGRAPH_SINGLETON_CONSTANT_LIST(V)
JSHeapBroker * broker
int32_t offset
Control control_
Effect effect_
Node * node
ZoneVector< RpoNumber > & result
ArgType first_arg
int position
Definition liveedit.cc:290
STL namespace.
typename GraphAssemblerLabelForXHelper< MachineRepresentation, void, Reps... >::Type GraphAssemblerLabelForReps
typename GraphAssemblerLabelForXHelper< Node *, void, Vars... >::Type GraphAssemblerLabelForVars
constexpr size_t kGraphAssemblerLabelDynamicCount
GraphAssemblerLabel< detail::kGraphAssemblerLabelDynamicCount > GraphAssemblerDynamicLabel
std::function< void(Node *)> NodeChangedCallback
NumberConstant(std::numeric_limits< double >::quiet_NaN())) DEFINE_GETTER(EmptyStateValues
constexpr int kSystemPointerSize
Definition globals.h:410
void Terminate(Isolate *isolate)
Definition bigint.cc:1187
i::Address Load(i::Address address)
Definition unwinder.cc:19
BytecodeSequenceNode * parent_
#define UNREACHABLE()
Definition logging.h:67
#define DCHECK_NULL(val)
Definition logging.h:491
#define CHECK(condition)
Definition logging.h:124
#define DCHECK_NOT_NULL(val)
Definition logging.h:492
#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
static Array< Node * > InitNodeArray(const Array< MachineRepresentation > &reps)
static Array< Node * > InitNodeArray(const Array< MachineRepresentation > &reps)
#define V8_INLINE
Definition v8config.h:500
#define V8_WARN_UNUSED_RESULT
Definition v8config.h:671
#define V8_NODISCARD
Definition v8config.h:693
wasm::ValueType type
MachineGraph * mcgraph_
WasmGraphAssembler gasm_