Bug Summary

File:out/../deps/v8/src/compiler/scheduler.cc
Warning:line 876, column 25
Access to field 'start' results in a dereference of a null pointer (loaded from variable 'loop')

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name scheduler.cc -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -ffunction-sections -fdata-sections -fcoverage-compilation-dir=/home/maurizio/node-v18.6.0/out -resource-dir /usr/local/lib/clang/16.0.0 -D _GLIBCXX_USE_CXX11_ABI=1 -D NODE_OPENSSL_CONF_NAME=nodejs_conf -D NODE_OPENSSL_HAS_QUIC -D V8_GYP_BUILD -D V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP=64 -D __STDC_FORMAT_MACROS -D OPENSSL_NO_PINSHARED -D OPENSSL_THREADS -D V8_TARGET_ARCH_X64 -D V8_HAVE_TARGET_OS -D V8_TARGET_OS_LINUX -D V8_EMBEDDER_STRING="-node.8" -D ENABLE_DISASSEMBLER -D V8_PROMISE_INTERNAL_FIELD_COUNT=1 -D V8_SHORT_BUILTIN_CALLS -D OBJECT_PRINT -D V8_INTL_SUPPORT -D V8_ATOMIC_OBJECT_FIELD_WRITES -D V8_ENABLE_LAZY_SOURCE_POSITIONS -D V8_USE_SIPHASH -D V8_SHARED_RO_HEAP -D V8_WIN64_UNWINDING_INFO -D V8_ENABLE_REGEXP_INTERPRETER_THREADED_DISPATCH -D V8_SNAPSHOT_COMPRESSION -D V8_ENABLE_WEBASSEMBLY -D V8_ENABLE_JAVASCRIPT_PROMISE_HOOKS -D V8_ALLOCATION_FOLDING -D V8_ALLOCATION_SITE_TRACKING -D V8_SCRIPTORMODULE_LEGACY_LIFETIME -D V8_ADVANCED_BIGINT_ALGORITHMS -D UCONFIG_NO_SERVICE=1 -D U_ENABLE_DYLOAD=0 -D U_STATIC_IMPLEMENTATION=1 -D U_HAVE_STD_STRING=1 -D UCONFIG_NO_BREAK_ITERATION=0 -I ../deps/v8 -I ../deps/v8/include -I /home/maurizio/node-v18.6.0/out/Release/obj/gen/generate-bytecode-output-root -I /home/maurizio/node-v18.6.0/out/Release/obj/gen -I ../deps/icu-small/source/i18n -I ../deps/icu-small/source/common -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/8/../../../../include/c++/8 -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/8/../../../../include/c++/8/x86_64-redhat-linux -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/8/../../../../include/c++/8/backward -internal-isystem /usr/local/lib/clang/16.0.0/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/8/../../../../x86_64-redhat-linux/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -O3 -Wno-unused-parameter -Wno-return-type -std=gnu++17 -fdeprecated-macro -fdebug-compilation-dir=/home/maurizio/node-v18.6.0/out -ferror-limit 19 -fno-rtti -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2022-08-22-142216-507842-1 -x c++ ../deps/v8/src/compiler/scheduler.cc
1// Copyright 2013 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "src/compiler/scheduler.h"
6
7#include <iomanip>
8
9#include "src/base/iterator.h"
10#include "src/builtins/profile-data-reader.h"
11#include "src/codegen/tick-counter.h"
12#include "src/compiler/common-operator.h"
13#include "src/compiler/control-equivalence.h"
14#include "src/compiler/graph.h"
15#include "src/compiler/node-marker.h"
16#include "src/compiler/node-properties.h"
17#include "src/compiler/node.h"
18#include "src/utils/bit-vector.h"
19#include "src/zone/zone-containers.h"
20
21namespace v8 {
22namespace internal {
23namespace compiler {
24
25#define TRACE(...) \
26 do { \
27 if (FLAG_trace_turbo_scheduler) PrintF(__VA_ARGS__); \
28 } while (false)
29
30Scheduler::Scheduler(Zone* zone, Graph* graph, Schedule* schedule, Flags flags,
31 size_t node_count_hint, TickCounter* tick_counter,
32 const ProfileDataFromFile* profile_data)
33 : zone_(zone),
34 graph_(graph),
35 schedule_(schedule),
36 flags_(flags),
37 scheduled_nodes_(zone),
38 schedule_root_nodes_(zone),
39 schedule_queue_(zone),
40 node_data_(zone),
41 tick_counter_(tick_counter),
42 profile_data_(profile_data),
43 common_dominator_cache_(zone) {
44 node_data_.reserve(node_count_hint);
45 node_data_.resize(graph->NodeCount(), DefaultSchedulerData());
46}
47
48Schedule* Scheduler::ComputeSchedule(Zone* zone, Graph* graph, Flags flags,
49 TickCounter* tick_counter,
50 const ProfileDataFromFile* profile_data) {
51 Zone* schedule_zone =
52 (flags & Scheduler::kTempSchedule) ? zone : graph->zone();
53
54 // Reserve 10% more space for nodes if node splitting is enabled to try to
55 // avoid resizing the vector since that would triple its zone memory usage.
56 float node_hint_multiplier = (flags & Scheduler::kSplitNodes) ? 1.1 : 1;
57 size_t node_count_hint = node_hint_multiplier * graph->NodeCount();
58
59 Schedule* schedule =
60 schedule_zone->New<Schedule>(schedule_zone, node_count_hint);
61 Scheduler scheduler(zone, graph, schedule, flags, node_count_hint,
62 tick_counter, profile_data);
63
64 scheduler.BuildCFG();
65 scheduler.ComputeSpecialRPONumbering();
66 scheduler.GenerateDominatorTree();
67
68 scheduler.PrepareUses();
69 scheduler.ScheduleEarly();
70 scheduler.ScheduleLate();
71
72 scheduler.SealFinalSchedule();
73
74 return schedule;
75}
76
77Scheduler::SchedulerData Scheduler::DefaultSchedulerData() {
78 SchedulerData def = {schedule_->start(), 0, kUnknown};
79 return def;
80}
81
82
83Scheduler::SchedulerData* Scheduler::GetData(Node* node) {
84 return &node_data_[node->id()];
85}
86
87Scheduler::Placement Scheduler::InitializePlacement(Node* node) {
88 SchedulerData* data = GetData(node);
89 if (data->placement_ == kFixed) {
90 // Nothing to do for control nodes that have been already fixed in
91 // the schedule.
92 return data->placement_;
93 }
94 DCHECK_EQ(kUnknown, data->placement_)((void) 0);
95 switch (node->opcode()) {
96 case IrOpcode::kParameter:
97 case IrOpcode::kOsrValue:
98 // Parameters and OSR values are always fixed to the start block.
99 data->placement_ = kFixed;
100 break;
101 case IrOpcode::kPhi:
102 case IrOpcode::kEffectPhi: {
103 // Phis and effect phis are fixed if their control inputs are, whereas
104 // otherwise they are coupled to a floating control node.
105 Placement p = GetPlacement(NodeProperties::GetControlInput(node));
106 data->placement_ = (p == kFixed ? kFixed : kCoupled);
107 break;
108 }
109 default:
110 // Control nodes that were not control-reachable from end may float.
111 data->placement_ = kSchedulable;
112 break;
113 }
114 return data->placement_;
115}
116
117Scheduler::Placement Scheduler::GetPlacement(Node* node) {
118 return GetData(node)->placement_;
119}
120
121bool Scheduler::IsLive(Node* node) { return GetPlacement(node) != kUnknown; }
122
123void Scheduler::UpdatePlacement(Node* node, Placement placement) {
124 SchedulerData* data = GetData(node);
125 if (data->placement_ == kUnknown) {
126 // We only update control nodes from {kUnknown} to {kFixed}. Ideally, we
127 // should check that {node} is a control node (including exceptional calls),
128 // but that is expensive.
129 DCHECK_EQ(Scheduler::kFixed, placement)((void) 0);
130 data->placement_ = placement;
131 return;
132 }
133
134 switch (node->opcode()) {
135 case IrOpcode::kParameter:
136 // Parameters are fixed once and for all.
137 UNREACHABLE()V8_Fatal("unreachable code");
138 case IrOpcode::kPhi:
139 case IrOpcode::kEffectPhi: {
140 // Phis and effect phis are coupled to their respective blocks.
141 DCHECK_EQ(Scheduler::kCoupled, data->placement_)((void) 0);
142 DCHECK_EQ(Scheduler::kFixed, placement)((void) 0);
143 Node* control = NodeProperties::GetControlInput(node);
144 BasicBlock* block = schedule_->block(control);
145 schedule_->AddNode(block, node);
146 break;
147 }
148#define DEFINE_CONTROL_CASE(V) case IrOpcode::k##V:
149 CONTROL_OP_LIST(DEFINE_CONTROL_CASE)DEFINE_CONTROL_CASE(Start) DEFINE_CONTROL_CASE(Loop) DEFINE_CONTROL_CASE
(Branch) DEFINE_CONTROL_CASE(Switch) DEFINE_CONTROL_CASE(IfTrue
) DEFINE_CONTROL_CASE(IfFalse) DEFINE_CONTROL_CASE(IfSuccess)
DEFINE_CONTROL_CASE(IfException) DEFINE_CONTROL_CASE(IfValue
) DEFINE_CONTROL_CASE(IfDefault) DEFINE_CONTROL_CASE(Merge) DEFINE_CONTROL_CASE
(Deoptimize) DEFINE_CONTROL_CASE(DeoptimizeIf) DEFINE_CONTROL_CASE
(DeoptimizeUnless) DEFINE_CONTROL_CASE(TrapIf) DEFINE_CONTROL_CASE
(TrapUnless) DEFINE_CONTROL_CASE(Return) DEFINE_CONTROL_CASE(
TailCall) DEFINE_CONTROL_CASE(Terminate) DEFINE_CONTROL_CASE(
Throw) DEFINE_CONTROL_CASE(End)
150#undef DEFINE_CONTROL_CASE
151 {
152 // Control nodes force coupled uses to be placed.
153 for (auto use : node->uses()) {
154 if (GetPlacement(use) == Scheduler::kCoupled) {
155 DCHECK_EQ(node, NodeProperties::GetControlInput(use))((void) 0);
156 UpdatePlacement(use, placement);
157 }
158 }
159 break;
160 }
161 default:
162 DCHECK_EQ(Scheduler::kSchedulable, data->placement_)((void) 0);
163 DCHECK_EQ(Scheduler::kScheduled, placement)((void) 0);
164 break;
165 }
166 // Reduce the use count of the node's inputs to potentially make them
167 // schedulable. If all the uses of a node have been scheduled, then the node
168 // itself can be scheduled.
169 base::Optional<int> coupled_control_edge = GetCoupledControlEdge(node);
170 for (Edge const edge : node->input_edges()) {
171 DCHECK_EQ(node, edge.from())((void) 0);
172 if (edge.index() != coupled_control_edge) {
173 DecrementUnscheduledUseCount(edge.to(), node);
174 }
175 }
176 data->placement_ = placement;
177}
178
179base::Optional<int> Scheduler::GetCoupledControlEdge(Node* node) {
180 if (GetPlacement(node) == kCoupled) {
181 return NodeProperties::FirstControlIndex(node);
182 }
183 return {};
184}
185
186void Scheduler::IncrementUnscheduledUseCount(Node* node, Node* from) {
187 // Tracking use counts for fixed nodes is useless.
188 if (GetPlacement(node) == kFixed) return;
189
190 // Use count for coupled nodes is summed up on their control.
191 if (GetPlacement(node) == kCoupled) {
192 node = NodeProperties::GetControlInput(node);
193 DCHECK_NE(GetPlacement(node), Placement::kFixed)((void) 0);
194 DCHECK_NE(GetPlacement(node), Placement::kCoupled)((void) 0);
195 }
196
197 ++(GetData(node)->unscheduled_count_);
198 if (FLAG_trace_turbo_scheduler) {
199 TRACE(" Use count of #%d:%s (used by #%d:%s)++ = %d\n", node->id(),
200 node->op()->mnemonic(), from->id(), from->op()->mnemonic(),
201 GetData(node)->unscheduled_count_);
202 }
203}
204
205void Scheduler::DecrementUnscheduledUseCount(Node* node, Node* from) {
206 // Tracking use counts for fixed nodes is useless.
207 if (GetPlacement(node) == kFixed) return;
208
209 // Use count for coupled nodes is summed up on their control.
210 if (GetPlacement(node) == kCoupled) {
211 node = NodeProperties::GetControlInput(node);
212 DCHECK_NE(GetPlacement(node), Placement::kFixed)((void) 0);
213 DCHECK_NE(GetPlacement(node), Placement::kCoupled)((void) 0);
214 }
215
216 DCHECK_LT(0, GetData(node)->unscheduled_count_)((void) 0);
217 --(GetData(node)->unscheduled_count_);
218 if (FLAG_trace_turbo_scheduler) {
219 TRACE(" Use count of #%d:%s (used by #%d:%s)-- = %d\n", node->id(),
220 node->op()->mnemonic(), from->id(), from->op()->mnemonic(),
221 GetData(node)->unscheduled_count_);
222 }
223 if (GetData(node)->unscheduled_count_ == 0) {
224 TRACE(" newly eligible #%d:%s\n", node->id(), node->op()->mnemonic());
225 schedule_queue_.push(node);
226 }
227}
228
229// -----------------------------------------------------------------------------
230// Phase 1: Build control-flow graph.
231
232
233// Internal class to build a control flow graph (i.e the basic blocks and edges
234// between them within a Schedule) from the node graph. Visits control edges of
235// the graph backwards from an end node in order to find the connected control
236// subgraph, needed for scheduling.
237class CFGBuilder : public ZoneObject {
238 public:
239 CFGBuilder(Zone* zone, Scheduler* scheduler)
240 : zone_(zone),
241 scheduler_(scheduler),
242 schedule_(scheduler->schedule_),
243 queued_(scheduler->graph_, 2),
244 queue_(zone),
245 control_(zone),
246 component_entry_(nullptr),
247 component_start_(nullptr),
248 component_end_(nullptr) {}
249
250 // Run the control flow graph construction algorithm by walking the graph
251 // backwards from end through control edges, building and connecting the
252 // basic blocks for control nodes.
253 void Run() {
254 ResetDataStructures();
255 Queue(scheduler_->graph_->end());
256
257 while (!queue_.empty()) { // Breadth-first backwards traversal.
258 scheduler_->tick_counter_->TickAndMaybeEnterSafepoint();
259 Node* node = queue_.front();
260 queue_.pop();
261 int max = NodeProperties::PastControlIndex(node);
262 for (int i = NodeProperties::FirstControlIndex(node); i < max; i++) {
263 Queue(node->InputAt(i));
264 }
265 }
266
267 for (NodeVector::iterator i = control_.begin(); i != control_.end(); ++i) {
268 ConnectBlocks(*i); // Connect block to its predecessor/successors.
269 }
270 }
271
272 // Run the control flow graph construction for a minimal control-connected
273 // component ending in {exit} and merge that component into an existing
274 // control flow graph at the bottom of {block}.
275 void Run(BasicBlock* block, Node* exit) {
276 ResetDataStructures();
277 Queue(exit);
278
279 component_entry_ = nullptr;
280 component_start_ = block;
281 component_end_ = schedule_->block(exit);
282 scheduler_->equivalence_->Run(exit);
283 while (!queue_.empty()) { // Breadth-first backwards traversal.
284 scheduler_->tick_counter_->TickAndMaybeEnterSafepoint();
285 Node* node = queue_.front();
286 queue_.pop();
287
288 // Use control dependence equivalence to find a canonical single-entry
289 // single-exit region that makes up a minimal component to be scheduled.
290 if (IsSingleEntrySingleExitRegion(node, exit)) {
291 TRACE("Found SESE at #%d:%s\n", node->id(), node->op()->mnemonic());
292 DCHECK(!component_entry_)((void) 0);
293 component_entry_ = node;
294 continue;
295 }
296
297 int max = NodeProperties::PastControlIndex(node);
298 for (int i = NodeProperties::FirstControlIndex(node); i < max; i++) {
299 Queue(node->InputAt(i));
300 }
301 }
302 DCHECK(component_entry_)((void) 0);
303
304 for (NodeVector::iterator i = control_.begin(); i != control_.end(); ++i) {
305 ConnectBlocks(*i); // Connect block to its predecessor/successors.
306 }
307 }
308
309 private:
310 friend class ScheduleLateNodeVisitor;
311 friend class Scheduler;
312
313 void FixNode(BasicBlock* block, Node* node) {
314 schedule_->AddNode(block, node);
315 scheduler_->UpdatePlacement(node, Scheduler::kFixed);
316 }
317
318 void Queue(Node* node) {
319 // Mark the connected control nodes as they are queued.
320 if (!queued_.Get(node)) {
321 BuildBlocks(node);
322 queue_.push(node);
323 queued_.Set(node, true);
324 control_.push_back(node);
325 }
326 }
327
328 void BuildBlocks(Node* node) {
329 switch (node->opcode()) {
330 case IrOpcode::kEnd:
331 FixNode(schedule_->end(), node);
332 break;
333 case IrOpcode::kStart:
334 FixNode(schedule_->start(), node);
335 break;
336 case IrOpcode::kLoop:
337 case IrOpcode::kMerge:
338 BuildBlockForNode(node);
339 break;
340 case IrOpcode::kTerminate: {
341 // Put Terminate in the loop to which it refers.
342 Node* loop = NodeProperties::GetControlInput(node);
343 BasicBlock* block = BuildBlockForNode(loop);
344 FixNode(block, node);
345 break;
346 }
347 case IrOpcode::kBranch:
348 case IrOpcode::kSwitch:
349 BuildBlocksForSuccessors(node);
350 break;
351#define BUILD_BLOCK_JS_CASE(Name, ...) case IrOpcode::k##Name:
352 JS_OP_LIST(BUILD_BLOCK_JS_CASE)BUILD_BLOCK_JS_CASE(JSEqual, Equal) BUILD_BLOCK_JS_CASE(JSStrictEqual
, StrictEqual) BUILD_BLOCK_JS_CASE(JSLessThan, LessThan) BUILD_BLOCK_JS_CASE
(JSGreaterThan, GreaterThan) BUILD_BLOCK_JS_CASE(JSLessThanOrEqual
, LessThanOrEqual) BUILD_BLOCK_JS_CASE(JSGreaterThanOrEqual, GreaterThanOrEqual
) BUILD_BLOCK_JS_CASE(JSBitwiseOr, BitwiseOr) BUILD_BLOCK_JS_CASE
(JSBitwiseXor, BitwiseXor) BUILD_BLOCK_JS_CASE(JSBitwiseAnd, BitwiseAnd
) BUILD_BLOCK_JS_CASE(JSShiftLeft, ShiftLeft) BUILD_BLOCK_JS_CASE
(JSShiftRight, ShiftRight) BUILD_BLOCK_JS_CASE(JSShiftRightLogical
, ShiftRightLogical) BUILD_BLOCK_JS_CASE(JSAdd, Add) BUILD_BLOCK_JS_CASE
(JSSubtract, Subtract) BUILD_BLOCK_JS_CASE(JSMultiply, Multiply
) BUILD_BLOCK_JS_CASE(JSDivide, Divide) BUILD_BLOCK_JS_CASE(JSModulus
, Modulus) BUILD_BLOCK_JS_CASE(JSExponentiate, Exponentiate) BUILD_BLOCK_JS_CASE
(JSHasInPrototypeChain) BUILD_BLOCK_JS_CASE(JSInstanceOf) BUILD_BLOCK_JS_CASE
(JSOrdinaryHasInstance) BUILD_BLOCK_JS_CASE(JSDecrement, Decrement
) BUILD_BLOCK_JS_CASE(JSIncrement, Increment) BUILD_BLOCK_JS_CASE
(JSBitwiseNot, BitwiseNot) BUILD_BLOCK_JS_CASE(JSNegate, Negate
) BUILD_BLOCK_JS_CASE(JSToLength) BUILD_BLOCK_JS_CASE(JSToName
) BUILD_BLOCK_JS_CASE(JSToNumber) BUILD_BLOCK_JS_CASE(JSToNumberConvertBigInt
) BUILD_BLOCK_JS_CASE(JSToNumeric) BUILD_BLOCK_JS_CASE(JSToObject
) BUILD_BLOCK_JS_CASE(JSToString) BUILD_BLOCK_JS_CASE(JSParseInt
) BUILD_BLOCK_JS_CASE(JSCloneObject) BUILD_BLOCK_JS_CASE(JSCreate
) BUILD_BLOCK_JS_CASE(JSCreateArguments) BUILD_BLOCK_JS_CASE(
JSCreateArray) BUILD_BLOCK_JS_CASE(JSCreateArrayFromIterable)
BUILD_BLOCK_JS_CASE(JSCreateArrayIterator) BUILD_BLOCK_JS_CASE
(JSCreateAsyncFunctionObject) BUILD_BLOCK_JS_CASE(JSCreateBoundFunction
) BUILD_BLOCK_JS_CASE(JSCreateClosure) BUILD_BLOCK_JS_CASE(JSCreateCollectionIterator
) BUILD_BLOCK_JS_CASE(JSCreateEmptyLiteralArray) BUILD_BLOCK_JS_CASE
(JSCreateEmptyLiteralObject) BUILD_BLOCK_JS_CASE(JSCreateGeneratorObject
) BUILD_BLOCK_JS_CASE(JSCreateIterResultObject) BUILD_BLOCK_JS_CASE
(JSCreateKeyValueArray) BUILD_BLOCK_JS_CASE(JSCreateLiteralArray
) BUILD_BLOCK_JS_CASE(JSCreateLiteralObject) BUILD_BLOCK_JS_CASE
(JSCreateLiteralRegExp) BUILD_BLOCK_JS_CASE(JSCreateObject) BUILD_BLOCK_JS_CASE
(JSCreatePromise) BUILD_BLOCK_JS_CASE(JSCreateStringIterator)
BUILD_BLOCK_JS_CASE(JSCreateTypedArray) BUILD_BLOCK_JS_CASE(
JSGetTemplateObject) BUILD_BLOCK_JS_CASE(JSLoadProperty) BUILD_BLOCK_JS_CASE
(JSLoadNamed) BUILD_BLOCK_JS_CASE(JSLoadNamedFromSuper) BUILD_BLOCK_JS_CASE
(JSLoadGlobal) BUILD_BLOCK_JS_CASE(JSSetKeyedProperty) BUILD_BLOCK_JS_CASE
(JSDefineKeyedOwnProperty) BUILD_BLOCK_JS_CASE(JSSetNamedProperty
) BUILD_BLOCK_JS_CASE(JSDefineNamedOwnProperty) BUILD_BLOCK_JS_CASE
(JSStoreGlobal) BUILD_BLOCK_JS_CASE(JSDefineKeyedOwnPropertyInLiteral
) BUILD_BLOCK_JS_CASE(JSStoreInArrayLiteral) BUILD_BLOCK_JS_CASE
(JSDeleteProperty) BUILD_BLOCK_JS_CASE(JSHasProperty) BUILD_BLOCK_JS_CASE
(JSGetSuperConstructor) BUILD_BLOCK_JS_CASE(JSHasContextExtension
) BUILD_BLOCK_JS_CASE(JSLoadContext) BUILD_BLOCK_JS_CASE(JSStoreContext
) BUILD_BLOCK_JS_CASE(JSCreateFunctionContext) BUILD_BLOCK_JS_CASE
(JSCreateCatchContext) BUILD_BLOCK_JS_CASE(JSCreateWithContext
) BUILD_BLOCK_JS_CASE(JSCreateBlockContext) BUILD_BLOCK_JS_CASE
(JSCall) BUILD_BLOCK_JS_CASE(JSCallForwardVarargs) BUILD_BLOCK_JS_CASE
(JSCallWithArrayLike) BUILD_BLOCK_JS_CASE(JSCallWithSpread) BUILD_BLOCK_JS_CASE
(JSWasmCall) BUILD_BLOCK_JS_CASE(JSConstructForwardVarargs) BUILD_BLOCK_JS_CASE
(JSConstruct) BUILD_BLOCK_JS_CASE(JSConstructWithArrayLike) BUILD_BLOCK_JS_CASE
(JSConstructWithSpread) BUILD_BLOCK_JS_CASE(JSAsyncFunctionEnter
) BUILD_BLOCK_JS_CASE(JSAsyncFunctionReject) BUILD_BLOCK_JS_CASE
(JSAsyncFunctionResolve) BUILD_BLOCK_JS_CASE(JSCallRuntime) BUILD_BLOCK_JS_CASE
(JSForInEnumerate) BUILD_BLOCK_JS_CASE(JSForInNext) BUILD_BLOCK_JS_CASE
(JSForInPrepare) BUILD_BLOCK_JS_CASE(JSGetIterator) BUILD_BLOCK_JS_CASE
(JSLoadMessage) BUILD_BLOCK_JS_CASE(JSStoreMessage) BUILD_BLOCK_JS_CASE
(JSLoadModule) BUILD_BLOCK_JS_CASE(JSStoreModule) BUILD_BLOCK_JS_CASE
(JSGetImportMeta) BUILD_BLOCK_JS_CASE(JSGeneratorStore) BUILD_BLOCK_JS_CASE
(JSGeneratorRestoreContinuation) BUILD_BLOCK_JS_CASE(JSGeneratorRestoreContext
) BUILD_BLOCK_JS_CASE(JSGeneratorRestoreRegister) BUILD_BLOCK_JS_CASE
(JSGeneratorRestoreInputOrDebugPos) BUILD_BLOCK_JS_CASE(JSFulfillPromise
) BUILD_BLOCK_JS_CASE(JSPerformPromiseThen) BUILD_BLOCK_JS_CASE
(JSPromiseResolve) BUILD_BLOCK_JS_CASE(JSRejectPromise) BUILD_BLOCK_JS_CASE
(JSResolvePromise) BUILD_BLOCK_JS_CASE(JSStackCheck) BUILD_BLOCK_JS_CASE
(JSObjectIsArray) BUILD_BLOCK_JS_CASE(JSRegExpTest) BUILD_BLOCK_JS_CASE
(JSDebugger)
353// JS opcodes are just like calls => fall through.
354#undef BUILD_BLOCK_JS_CASE
355 case IrOpcode::kCall:
356 if (NodeProperties::IsExceptionalCall(node)) {
357 BuildBlocksForSuccessors(node);
358 }
359 break;
360 default:
361 break;
362 }
363 }
364
365 void ConnectBlocks(Node* node) {
366 switch (node->opcode()) {
367 case IrOpcode::kLoop:
368 case IrOpcode::kMerge:
369 ConnectMerge(node);
370 break;
371 case IrOpcode::kBranch:
372 scheduler_->UpdatePlacement(node, Scheduler::kFixed);
373 ConnectBranch(node);
374 break;
375 case IrOpcode::kSwitch:
376 scheduler_->UpdatePlacement(node, Scheduler::kFixed);
377 ConnectSwitch(node);
378 break;
379 case IrOpcode::kDeoptimize:
380 scheduler_->UpdatePlacement(node, Scheduler::kFixed);
381 ConnectDeoptimize(node);
382 break;
383 case IrOpcode::kTailCall:
384 scheduler_->UpdatePlacement(node, Scheduler::kFixed);
385 ConnectTailCall(node);
386 break;
387 case IrOpcode::kReturn:
388 scheduler_->UpdatePlacement(node, Scheduler::kFixed);
389 ConnectReturn(node);
390 break;
391 case IrOpcode::kThrow:
392 scheduler_->UpdatePlacement(node, Scheduler::kFixed);
393 ConnectThrow(node);
394 break;
395#define CONNECT_BLOCK_JS_CASE(Name, ...) case IrOpcode::k##Name:
396 JS_OP_LIST(CONNECT_BLOCK_JS_CASE)CONNECT_BLOCK_JS_CASE(JSEqual, Equal) CONNECT_BLOCK_JS_CASE(JSStrictEqual
, StrictEqual) CONNECT_BLOCK_JS_CASE(JSLessThan, LessThan) CONNECT_BLOCK_JS_CASE
(JSGreaterThan, GreaterThan) CONNECT_BLOCK_JS_CASE(JSLessThanOrEqual
, LessThanOrEqual) CONNECT_BLOCK_JS_CASE(JSGreaterThanOrEqual
, GreaterThanOrEqual) CONNECT_BLOCK_JS_CASE(JSBitwiseOr, BitwiseOr
) CONNECT_BLOCK_JS_CASE(JSBitwiseXor, BitwiseXor) CONNECT_BLOCK_JS_CASE
(JSBitwiseAnd, BitwiseAnd) CONNECT_BLOCK_JS_CASE(JSShiftLeft,
ShiftLeft) CONNECT_BLOCK_JS_CASE(JSShiftRight, ShiftRight) CONNECT_BLOCK_JS_CASE
(JSShiftRightLogical, ShiftRightLogical) CONNECT_BLOCK_JS_CASE
(JSAdd, Add) CONNECT_BLOCK_JS_CASE(JSSubtract, Subtract) CONNECT_BLOCK_JS_CASE
(JSMultiply, Multiply) CONNECT_BLOCK_JS_CASE(JSDivide, Divide
) CONNECT_BLOCK_JS_CASE(JSModulus, Modulus) CONNECT_BLOCK_JS_CASE
(JSExponentiate, Exponentiate) CONNECT_BLOCK_JS_CASE(JSHasInPrototypeChain
) CONNECT_BLOCK_JS_CASE(JSInstanceOf) CONNECT_BLOCK_JS_CASE(JSOrdinaryHasInstance
) CONNECT_BLOCK_JS_CASE(JSDecrement, Decrement) CONNECT_BLOCK_JS_CASE
(JSIncrement, Increment) CONNECT_BLOCK_JS_CASE(JSBitwiseNot, BitwiseNot
) CONNECT_BLOCK_JS_CASE(JSNegate, Negate) CONNECT_BLOCK_JS_CASE
(JSToLength) CONNECT_BLOCK_JS_CASE(JSToName) CONNECT_BLOCK_JS_CASE
(JSToNumber) CONNECT_BLOCK_JS_CASE(JSToNumberConvertBigInt) CONNECT_BLOCK_JS_CASE
(JSToNumeric) CONNECT_BLOCK_JS_CASE(JSToObject) CONNECT_BLOCK_JS_CASE
(JSToString) CONNECT_BLOCK_JS_CASE(JSParseInt) CONNECT_BLOCK_JS_CASE
(JSCloneObject) CONNECT_BLOCK_JS_CASE(JSCreate) CONNECT_BLOCK_JS_CASE
(JSCreateArguments) CONNECT_BLOCK_JS_CASE(JSCreateArray) CONNECT_BLOCK_JS_CASE
(JSCreateArrayFromIterable) CONNECT_BLOCK_JS_CASE(JSCreateArrayIterator
) CONNECT_BLOCK_JS_CASE(JSCreateAsyncFunctionObject) CONNECT_BLOCK_JS_CASE
(JSCreateBoundFunction) CONNECT_BLOCK_JS_CASE(JSCreateClosure
) CONNECT_BLOCK_JS_CASE(JSCreateCollectionIterator) CONNECT_BLOCK_JS_CASE
(JSCreateEmptyLiteralArray) CONNECT_BLOCK_JS_CASE(JSCreateEmptyLiteralObject
) CONNECT_BLOCK_JS_CASE(JSCreateGeneratorObject) CONNECT_BLOCK_JS_CASE
(JSCreateIterResultObject) CONNECT_BLOCK_JS_CASE(JSCreateKeyValueArray
) CONNECT_BLOCK_JS_CASE(JSCreateLiteralArray) CONNECT_BLOCK_JS_CASE
(JSCreateLiteralObject) CONNECT_BLOCK_JS_CASE(JSCreateLiteralRegExp
) CONNECT_BLOCK_JS_CASE(JSCreateObject) CONNECT_BLOCK_JS_CASE
(JSCreatePromise) CONNECT_BLOCK_JS_CASE(JSCreateStringIterator
) CONNECT_BLOCK_JS_CASE(JSCreateTypedArray) CONNECT_BLOCK_JS_CASE
(JSGetTemplateObject) CONNECT_BLOCK_JS_CASE(JSLoadProperty) CONNECT_BLOCK_JS_CASE
(JSLoadNamed) CONNECT_BLOCK_JS_CASE(JSLoadNamedFromSuper) CONNECT_BLOCK_JS_CASE
(JSLoadGlobal) CONNECT_BLOCK_JS_CASE(JSSetKeyedProperty) CONNECT_BLOCK_JS_CASE
(JSDefineKeyedOwnProperty) CONNECT_BLOCK_JS_CASE(JSSetNamedProperty
) CONNECT_BLOCK_JS_CASE(JSDefineNamedOwnProperty) CONNECT_BLOCK_JS_CASE
(JSStoreGlobal) CONNECT_BLOCK_JS_CASE(JSDefineKeyedOwnPropertyInLiteral
) CONNECT_BLOCK_JS_CASE(JSStoreInArrayLiteral) CONNECT_BLOCK_JS_CASE
(JSDeleteProperty) CONNECT_BLOCK_JS_CASE(JSHasProperty) CONNECT_BLOCK_JS_CASE
(JSGetSuperConstructor) CONNECT_BLOCK_JS_CASE(JSHasContextExtension
) CONNECT_BLOCK_JS_CASE(JSLoadContext) CONNECT_BLOCK_JS_CASE(
JSStoreContext) CONNECT_BLOCK_JS_CASE(JSCreateFunctionContext
) CONNECT_BLOCK_JS_CASE(JSCreateCatchContext) CONNECT_BLOCK_JS_CASE
(JSCreateWithContext) CONNECT_BLOCK_JS_CASE(JSCreateBlockContext
) CONNECT_BLOCK_JS_CASE(JSCall) CONNECT_BLOCK_JS_CASE(JSCallForwardVarargs
) CONNECT_BLOCK_JS_CASE(JSCallWithArrayLike) CONNECT_BLOCK_JS_CASE
(JSCallWithSpread) CONNECT_BLOCK_JS_CASE(JSWasmCall) CONNECT_BLOCK_JS_CASE
(JSConstructForwardVarargs) CONNECT_BLOCK_JS_CASE(JSConstruct
) CONNECT_BLOCK_JS_CASE(JSConstructWithArrayLike) CONNECT_BLOCK_JS_CASE
(JSConstructWithSpread) CONNECT_BLOCK_JS_CASE(JSAsyncFunctionEnter
) CONNECT_BLOCK_JS_CASE(JSAsyncFunctionReject) CONNECT_BLOCK_JS_CASE
(JSAsyncFunctionResolve) CONNECT_BLOCK_JS_CASE(JSCallRuntime)
CONNECT_BLOCK_JS_CASE(JSForInEnumerate) CONNECT_BLOCK_JS_CASE
(JSForInNext) CONNECT_BLOCK_JS_CASE(JSForInPrepare) CONNECT_BLOCK_JS_CASE
(JSGetIterator) CONNECT_BLOCK_JS_CASE(JSLoadMessage) CONNECT_BLOCK_JS_CASE
(JSStoreMessage) CONNECT_BLOCK_JS_CASE(JSLoadModule) CONNECT_BLOCK_JS_CASE
(JSStoreModule) CONNECT_BLOCK_JS_CASE(JSGetImportMeta) CONNECT_BLOCK_JS_CASE
(JSGeneratorStore) CONNECT_BLOCK_JS_CASE(JSGeneratorRestoreContinuation
) CONNECT_BLOCK_JS_CASE(JSGeneratorRestoreContext) CONNECT_BLOCK_JS_CASE
(JSGeneratorRestoreRegister) CONNECT_BLOCK_JS_CASE(JSGeneratorRestoreInputOrDebugPos
) CONNECT_BLOCK_JS_CASE(JSFulfillPromise) CONNECT_BLOCK_JS_CASE
(JSPerformPromiseThen) CONNECT_BLOCK_JS_CASE(JSPromiseResolve
) CONNECT_BLOCK_JS_CASE(JSRejectPromise) CONNECT_BLOCK_JS_CASE
(JSResolvePromise) CONNECT_BLOCK_JS_CASE(JSStackCheck) CONNECT_BLOCK_JS_CASE
(JSObjectIsArray) CONNECT_BLOCK_JS_CASE(JSRegExpTest) CONNECT_BLOCK_JS_CASE
(JSDebugger)
397// JS opcodes are just like calls => fall through.
398#undef CONNECT_BLOCK_JS_CASE
399 case IrOpcode::kCall:
400 if (NodeProperties::IsExceptionalCall(node)) {
401 scheduler_->UpdatePlacement(node, Scheduler::kFixed);
402 ConnectCall(node);
403 }
404 break;
405 default:
406 break;
407 }
408 }
409
410 BasicBlock* BuildBlockForNode(Node* node) {
411 BasicBlock* block = schedule_->block(node);
412 if (block == nullptr) {
413 block = schedule_->NewBasicBlock();
414 TRACE("Create block id:%d for #%d:%s\n", block->id().ToInt(), node->id(),
415 node->op()->mnemonic());
416 FixNode(block, node);
417 }
418 return block;
419 }
420
421 void BuildBlocksForSuccessors(Node* node) {
422 size_t const successor_cnt = node->op()->ControlOutputCount();
423 Node** successors = zone_->NewArray<Node*>(successor_cnt);
424 NodeProperties::CollectControlProjections(node, successors, successor_cnt);
425 for (size_t index = 0; index < successor_cnt; ++index) {
426 BuildBlockForNode(successors[index]);
427 }
428 }
429
430 void CollectSuccessorBlocks(Node* node, BasicBlock** successor_blocks,
431 size_t successor_cnt) {
432 Node** successors = reinterpret_cast<Node**>(successor_blocks);
433 NodeProperties::CollectControlProjections(node, successors, successor_cnt);
434 for (size_t index = 0; index < successor_cnt; ++index) {
435 successor_blocks[index] = schedule_->block(successors[index]);
436 }
437 }
438
439 BasicBlock* FindPredecessorBlock(Node* node) {
440 BasicBlock* predecessor_block = nullptr;
441 while (true) {
442 predecessor_block = schedule_->block(node);
443 if (predecessor_block != nullptr) break;
444 node = NodeProperties::GetControlInput(node);
445 }
446 return predecessor_block;
447 }
448
449 void ConnectCall(Node* call) {
450 BasicBlock* successor_blocks[2];
451 CollectSuccessorBlocks(call, successor_blocks, arraysize(successor_blocks)(sizeof(ArraySizeHelper(successor_blocks))));
452
453 // Consider the exception continuation to be deferred.
454 successor_blocks[1]->set_deferred(true);
455
456 Node* call_control = NodeProperties::GetControlInput(call);
457 BasicBlock* call_block = FindPredecessorBlock(call_control);
458 TraceConnect(call, call_block, successor_blocks[0]);
459 TraceConnect(call, call_block, successor_blocks[1]);
460 schedule_->AddCall(call_block, call, successor_blocks[0],
461 successor_blocks[1]);
462 }
463
464 void ConnectBranch(Node* branch) {
465 BasicBlock* successor_blocks[2];
466 CollectSuccessorBlocks(branch, successor_blocks,
467 arraysize(successor_blocks)(sizeof(ArraySizeHelper(successor_blocks))));
468
469 BranchHint hint_from_profile = BranchHint::kNone;
470 if (const ProfileDataFromFile* profile_data = scheduler_->profile_data()) {
471 double block_zero_count =
472 profile_data->GetCounter(successor_blocks[0]->id().ToSize());
473 double block_one_count =
474 profile_data->GetCounter(successor_blocks[1]->id().ToSize());
475 // If a branch is visited a non-trivial number of times and substantially
476 // more often than its alternative, then mark it as likely.
477 constexpr double kMinimumCount = 100000;
478 constexpr double kThresholdRatio = 4000;
479 if (block_zero_count > kMinimumCount &&
480 block_zero_count / kThresholdRatio > block_one_count) {
481 hint_from_profile = BranchHint::kTrue;
482 } else if (block_one_count > kMinimumCount &&
483 block_one_count / kThresholdRatio > block_zero_count) {
484 hint_from_profile = BranchHint::kFalse;
485 }
486 }
487
488 // Consider branch hints.
489 switch (hint_from_profile) {
490 case BranchHint::kNone:
491 switch (BranchHintOf(branch->op())) {
492 case BranchHint::kNone:
493 break;
494 case BranchHint::kTrue:
495 successor_blocks[1]->set_deferred(true);
496 break;
497 case BranchHint::kFalse:
498 successor_blocks[0]->set_deferred(true);
499 break;
500 }
501 break;
502 case BranchHint::kTrue:
503 successor_blocks[1]->set_deferred(true);
504 break;
505 case BranchHint::kFalse:
506 successor_blocks[0]->set_deferred(true);
507 break;
508 }
509
510 if (hint_from_profile != BranchHint::kNone &&
511 BranchHintOf(branch->op()) != BranchHint::kNone &&
512 hint_from_profile != BranchHintOf(branch->op())) {
513 PrintF("Warning: profiling data overrode manual branch hint.\n");
514 }
515
516 if (branch == component_entry_) {
517 TraceConnect(branch, component_start_, successor_blocks[0]);
518 TraceConnect(branch, component_start_, successor_blocks[1]);
519 schedule_->InsertBranch(component_start_, component_end_, branch,
520 successor_blocks[0], successor_blocks[1]);
521 } else {
522 Node* branch_control = NodeProperties::GetControlInput(branch);
523 BasicBlock* branch_block = FindPredecessorBlock(branch_control);
524 TraceConnect(branch, branch_block, successor_blocks[0]);
525 TraceConnect(branch, branch_block, successor_blocks[1]);
526 schedule_->AddBranch(branch_block, branch, successor_blocks[0],
527 successor_blocks[1]);
528 }
529 }
530
531 void ConnectSwitch(Node* sw) {
532 size_t const successor_count = sw->op()->ControlOutputCount();
533 BasicBlock** successor_blocks =
534 zone_->NewArray<BasicBlock*>(successor_count);
535 CollectSuccessorBlocks(sw, successor_blocks, successor_count);
536
537 if (sw == component_entry_) {
538 for (size_t index = 0; index < successor_count; ++index) {
539 TraceConnect(sw, component_start_, successor_blocks[index]);
540 }
541 schedule_->InsertSwitch(component_start_, component_end_, sw,
542 successor_blocks, successor_count);
543 } else {
544 Node* switch_control = NodeProperties::GetControlInput(sw);
545 BasicBlock* switch_block = FindPredecessorBlock(switch_control);
546 for (size_t index = 0; index < successor_count; ++index) {
547 TraceConnect(sw, switch_block, successor_blocks[index]);
548 }
549 schedule_->AddSwitch(switch_block, sw, successor_blocks, successor_count);
550 }
551 for (size_t index = 0; index < successor_count; ++index) {
552 if (BranchHintOf(successor_blocks[index]->front()->op()) ==
553 BranchHint::kFalse) {
554 successor_blocks[index]->set_deferred(true);
555 }
556 }
557 }
558
559 void ConnectMerge(Node* merge) {
560 // Don't connect the special merge at the end to its predecessors.
561 if (IsFinalMerge(merge)) return;
562
563 BasicBlock* block = schedule_->block(merge);
564 DCHECK_NOT_NULL(block)((void) 0);
565 // For all of the merge's control inputs, add a goto at the end to the
566 // merge's basic block.
567 for (Node* const input : merge->inputs()) {
568 BasicBlock* predecessor_block = FindPredecessorBlock(input);
569 TraceConnect(merge, predecessor_block, block);
570 schedule_->AddGoto(predecessor_block, block);
571 }
572 }
573
574 void ConnectTailCall(Node* call) {
575 Node* call_control = NodeProperties::GetControlInput(call);
576 BasicBlock* call_block = FindPredecessorBlock(call_control);
577 TraceConnect(call, call_block, nullptr);
578 schedule_->AddTailCall(call_block, call);
579 }
580
581 void ConnectReturn(Node* ret) {
582 Node* return_control = NodeProperties::GetControlInput(ret);
583 BasicBlock* return_block = FindPredecessorBlock(return_control);
584 TraceConnect(ret, return_block, nullptr);
585 schedule_->AddReturn(return_block, ret);
586 }
587
588 void ConnectDeoptimize(Node* deopt) {
589 Node* deoptimize_control = NodeProperties::GetControlInput(deopt);
590 BasicBlock* deoptimize_block = FindPredecessorBlock(deoptimize_control);
591 TraceConnect(deopt, deoptimize_block, nullptr);
592 schedule_->AddDeoptimize(deoptimize_block, deopt);
593 }
594
595 void ConnectThrow(Node* thr) {
596 Node* throw_control = NodeProperties::GetControlInput(thr);
597 BasicBlock* throw_block = FindPredecessorBlock(throw_control);
598 TraceConnect(thr, throw_block, nullptr);
599 schedule_->AddThrow(throw_block, thr);
600 }
601
602 void TraceConnect(Node* node, BasicBlock* block, BasicBlock* succ) {
603 DCHECK_NOT_NULL(block)((void) 0);
604 if (succ == nullptr) {
605 TRACE("Connect #%d:%s, id:%d -> end\n", node->id(),
606 node->op()->mnemonic(), block->id().ToInt());
607 } else {
608 TRACE("Connect #%d:%s, id:%d -> id:%d\n", node->id(),
609 node->op()->mnemonic(), block->id().ToInt(), succ->id().ToInt());
610 }
611 }
612
613 bool IsFinalMerge(Node* node) {
614 return (node->opcode() == IrOpcode::kMerge &&
615 node == scheduler_->graph_->end()->InputAt(0));
616 }
617
618 bool IsSingleEntrySingleExitRegion(Node* entry, Node* exit) const {
619 size_t entry_class = scheduler_->equivalence_->ClassOf(entry);
620 size_t exit_class = scheduler_->equivalence_->ClassOf(exit);
621 return entry != exit && entry_class == exit_class;
622 }
623
624 void ResetDataStructures() {
625 control_.clear();
626 DCHECK(queue_.empty())((void) 0);
627 DCHECK(control_.empty())((void) 0);
628 }
629
630 Zone* zone_;
631 Scheduler* scheduler_;
632 Schedule* schedule_;
633 NodeMarker<bool> queued_; // Mark indicating whether node is queued.
634 ZoneQueue<Node*> queue_; // Queue used for breadth-first traversal.
635 NodeVector control_; // List of encountered control nodes.
636 Node* component_entry_; // Component single-entry node.
637 BasicBlock* component_start_; // Component single-entry block.
638 BasicBlock* component_end_; // Component single-exit block.
639};
640
641
642void Scheduler::BuildCFG() {
643 TRACE("--- CREATING CFG -------------------------------------------\n");
644
645 // Instantiate a new control equivalence algorithm for the graph.
646 equivalence_ = zone_->New<ControlEquivalence>(zone_, graph_);
647
648 // Build a control-flow graph for the main control-connected component that
649 // is being spanned by the graph's start and end nodes.
650 control_flow_builder_ = zone_->New<CFGBuilder>(zone_, this);
651 control_flow_builder_->Run();
652
653 // Initialize per-block data.
654 // Reserve an extra 10% to avoid resizing vector when fusing floating control.
655 scheduled_nodes_.reserve(schedule_->BasicBlockCount() * 1.1);
656 scheduled_nodes_.resize(schedule_->BasicBlockCount());
657}
658
659
660// -----------------------------------------------------------------------------
661// Phase 2: Compute special RPO and dominator tree.
662
663
664// Compute the special reverse-post-order block ordering, which is essentially
665// a RPO of the graph where loop bodies are contiguous. Properties:
666// 1. If block A is a predecessor of B, then A appears before B in the order,
667// unless B is a loop header and A is in the loop headed at B
668// (i.e. A -> B is a backedge).
669// => If block A dominates block B, then A appears before B in the order.
670// => If block A is a loop header, A appears before all blocks in the loop
671// headed at A.
672// 2. All loops are contiguous in the order (i.e. no intervening blocks that
673// do not belong to the loop.)
674// Note a simple RPO traversal satisfies (1) but not (2).
675class SpecialRPONumberer : public ZoneObject {
676 public:
677 SpecialRPONumberer(Zone* zone, Schedule* schedule)
678 : zone_(zone),
679 schedule_(schedule),
680 order_(nullptr),
681 beyond_end_(nullptr),
682 loops_(zone),
683 backedges_(zone),
684 stack_(zone),
685 previous_block_count_(0),
686 empty_(0, zone) {}
687
688 // Computes the special reverse-post-order for the main control flow graph,
689 // that is for the graph spanned between the schedule's start and end blocks.
690 void ComputeSpecialRPO() {
691 DCHECK_EQ(0, schedule_->end()->SuccessorCount())((void) 0);
692 DCHECK(!order_)((void) 0); // Main order does not exist yet.
693 ComputeAndInsertSpecialRPO(schedule_->start(), schedule_->end());
694 }
695
696 // Computes the special reverse-post-order for a partial control flow graph,
697 // that is for the graph spanned between the given {entry} and {end} blocks,
698 // then updates the existing ordering with this new information.
699 void UpdateSpecialRPO(BasicBlock* entry, BasicBlock* end) {
700 DCHECK(order_)((void) 0); // Main order to be updated is present.
701 ComputeAndInsertSpecialRPO(entry, end);
702 }
703
704 // Serialize the previously computed order as a special reverse-post-order
705 // numbering for basic blocks into the final schedule.
706 void SerializeRPOIntoSchedule() {
707 int32_t number = 0;
708 for (BasicBlock* b = order_; b != nullptr; b = b->rpo_next()) {
709 b->set_rpo_number(number++);
710 schedule_->rpo_order()->push_back(b);
711 }
712 BeyondEndSentinel()->set_rpo_number(number);
713 }
714
715 // Print and verify the special reverse-post-order.
716 void PrintAndVerifySpecialRPO() {
717#if DEBUG
718 if (FLAG_trace_turbo_scheduler) PrintRPO();
719 VerifySpecialRPO();
720#endif
721 }
722
723 const ZoneVector<BasicBlock*>& GetOutgoingBlocks(BasicBlock* block) {
724 if (HasLoopNumber(block)) {
725 LoopInfo const& loop = loops_[GetLoopNumber(block)];
726 if (loop.outgoing) return *loop.outgoing;
727 }
728 return empty_;
729 }
730
731 bool HasLoopBlocks() const { return loops_.size() != 0; }
732
733 private:
734 using Backedge = std::pair<BasicBlock*, size_t>;
735
736 // Numbering for BasicBlock::rpo_number for this block traversal:
737 static const int kBlockOnStack = -2;
738 static const int kBlockVisited1 = -3;
739 static const int kBlockVisited2 = -4;
740 static const int kBlockUnvisited1 = -1;
741 static const int kBlockUnvisited2 = kBlockVisited1;
742
743 struct SpecialRPOStackFrame {
744 BasicBlock* block;
745 size_t index;
746 };
747
748 struct LoopInfo {
749 BasicBlock* header;
750 ZoneVector<BasicBlock*>* outgoing;
751 BitVector* members;
752 LoopInfo* prev;
753 BasicBlock* end;
754 BasicBlock* start;
755
756 void AddOutgoing(Zone* zone, BasicBlock* block) {
757 if (outgoing == nullptr) {
758 outgoing = zone->New<ZoneVector<BasicBlock*>>(zone);
759 }
760 outgoing->push_back(block);
761 }
762 };
763
764 int Push(int depth, BasicBlock* child, int unvisited) {
765 if (child->rpo_number() == unvisited) {
766 stack_[depth].block = child;
767 stack_[depth].index = 0;
768 child->set_rpo_number(kBlockOnStack);
769 return depth + 1;
770 }
771 return depth;
772 }
773
774 BasicBlock* PushFront(BasicBlock* head, BasicBlock* block) {
775 block->set_rpo_next(head);
776 return block;
777 }
778
779 static int GetLoopNumber(BasicBlock* block) { return block->loop_number(); }
780 static void SetLoopNumber(BasicBlock* block, int loop_number) {
781 return block->set_loop_number(loop_number);
782 }
783 static bool HasLoopNumber(BasicBlock* block) {
784 return block->loop_number() >= 0;
785 }
786
787 // We only need this special sentinel because some tests use the schedule's
788 // end block in actual control flow (e.g. with end having successors).
789 BasicBlock* BeyondEndSentinel() {
790 if (beyond_end_ == nullptr) {
791 BasicBlock::Id id = BasicBlock::Id::FromInt(-1);
792 beyond_end_ = schedule_->zone()->New<BasicBlock>(schedule_->zone(), id);
793 }
794 return beyond_end_;
795 }
796
797 // Compute special RPO for the control flow graph between {entry} and {end},
798 // mutating any existing order so that the result is still valid.
799 void ComputeAndInsertSpecialRPO(BasicBlock* entry, BasicBlock* end) {
800 // RPO should not have been serialized for this schedule yet.
801 CHECK_EQ(kBlockUnvisited1, schedule_->start()->loop_number())do { bool _cmp = ::v8::base::CmpEQImpl< typename ::v8::base
::pass_value_or_ref<decltype(kBlockUnvisited1)>::type, typename
::v8::base::pass_value_or_ref<decltype(schedule_->start
()->loop_number())>::type>((kBlockUnvisited1), (schedule_
->start()->loop_number())); do { if ((__builtin_expect(
!!(!(_cmp)), 0))) { V8_Fatal("Check failed: %s.", "kBlockUnvisited1"
" " "==" " " "schedule_->start()->loop_number()"); } }
while (false); } while (false)
;
1
Taking false branch
2
Loop condition is false. Exiting loop
3
Loop condition is false. Exiting loop
802 CHECK_EQ(kBlockUnvisited1, schedule_->start()->rpo_number())do { bool _cmp = ::v8::base::CmpEQImpl< typename ::v8::base
::pass_value_or_ref<decltype(kBlockUnvisited1)>::type, typename
::v8::base::pass_value_or_ref<decltype(schedule_->start
()->rpo_number())>::type>((kBlockUnvisited1), (schedule_
->start()->rpo_number())); do { if ((__builtin_expect(!
!(!(_cmp)), 0))) { V8_Fatal("Check failed: %s.", "kBlockUnvisited1"
" " "==" " " "schedule_->start()->rpo_number()"); } } while
(false); } while (false)
;
4
Taking false branch
5
Loop condition is false. Exiting loop
6
Loop condition is false. Exiting loop
803 CHECK_EQ(0, static_cast<int>(schedule_->rpo_order()->size()))do { bool _cmp = ::v8::base::CmpEQImpl< typename ::v8::base
::pass_value_or_ref<decltype(0)>::type, typename ::v8::
base::pass_value_or_ref<decltype(static_cast<int>(schedule_
->rpo_order()->size()))>::type>((0), (static_cast
<int>(schedule_->rpo_order()->size()))); do { if (
(__builtin_expect(!!(!(_cmp)), 0))) { V8_Fatal("Check failed: %s."
, "0" " " "==" " " "static_cast<int>(schedule_->rpo_order()->size())"
); } } while (false); } while (false)
;
7
Taking false branch
8
Loop condition is false. Exiting loop
9
Loop condition is false. Exiting loop
804
805 // Find correct insertion point within existing order.
806 BasicBlock* insertion_point = entry->rpo_next();
807 BasicBlock* order = insertion_point;
808
809 // Perform an iterative RPO traversal using an explicit stack,
810 // recording backedges that form cycles. O(|B|).
811 DCHECK_LT(previous_block_count_, schedule_->BasicBlockCount())((void) 0);
812 stack_.resize(schedule_->BasicBlockCount() - previous_block_count_);
813 previous_block_count_ = schedule_->BasicBlockCount();
814 int stack_depth = Push(0, entry, kBlockUnvisited1);
815 int num_loops = static_cast<int>(loops_.size());
816
817 while (stack_depth > 0) {
10
Loop condition is false. Execution continues on line 847
818 int current = stack_depth - 1;
819 SpecialRPOStackFrame* frame = &stack_[current];
820
821 if (frame->block != end &&
822 frame->index < frame->block->SuccessorCount()) {
823 // Process the next successor.
824 BasicBlock* succ = frame->block->SuccessorAt(frame->index++);
825 if (succ->rpo_number() == kBlockVisited1) continue;
826 if (succ->rpo_number() == kBlockOnStack) {
827 // The successor is on the stack, so this is a backedge (cycle).
828 backedges_.push_back(Backedge(frame->block, frame->index - 1));
829 if (!HasLoopNumber(succ)) {
830 // Assign a new loop number to the header if it doesn't have one.
831 SetLoopNumber(succ, num_loops++);
832 }
833 } else {
834 // Push the successor onto the stack.
835 DCHECK_EQ(kBlockUnvisited1, succ->rpo_number())((void) 0);
836 stack_depth = Push(stack_depth, succ, kBlockUnvisited1);
837 }
838 } else {
839 // Finished with all successors; pop the stack and add the block.
840 order = PushFront(order, frame->block);
841 frame->block->set_rpo_number(kBlockVisited1);
842 stack_depth--;
843 }
844 }
845
846 // If no loops were encountered, then the order we computed was correct.
847 if (num_loops > static_cast<int>(loops_.size())) {
11
Assuming the condition is true
12
Taking true branch
848 // Otherwise, compute the loop information from the backedges in order
849 // to perform a traversal that groups loop bodies together.
850 ComputeLoopInfo(&stack_, num_loops, &backedges_);
851
852 // Initialize the "loop stack". Note the entry could be a loop header.
853 LoopInfo* loop =
14
'loop' initialized to a null pointer value
854 HasLoopNumber(entry) ? &loops_[GetLoopNumber(entry)] : nullptr;
13
'?' condition is false
855 order = insertion_point;
856
857 // Perform an iterative post-order traversal, visiting loop bodies before
858 // edges that lead out of loops. Visits each block once, but linking loop
859 // sections together is linear in the loop size, so overall is
860 // O(|B| + max(loop_depth) * max(|loop|))
861 stack_depth = Push(0, entry, kBlockUnvisited2);
862 while (stack_depth > 0) {
15
Loop condition is true. Entering loop body
863 SpecialRPOStackFrame* frame = &stack_[stack_depth - 1];
864 BasicBlock* block = frame->block;
865 BasicBlock* succ = nullptr;
866
867 if (block != end && frame->index < block->SuccessorCount()) {
16
Assuming 'block' is equal to 'end'
868 // Process the next normal successor.
869 succ = block->SuccessorAt(frame->index++);
870 } else if (HasLoopNumber(block)) {
17
Taking true branch
871 // Process additional outgoing edges from the loop header.
872 if (block->rpo_number() == kBlockOnStack) {
18
Assuming the condition is true
19
Taking true branch
873 // Finish the loop body the first time the header is left on the
874 // stack.
875 DCHECK(loop != nullptr && loop->header == block)((void) 0);
876 loop->start = PushFront(order, block);
20
Access to field 'start' results in a dereference of a null pointer (loaded from variable 'loop')
877 order = loop->end;
878 block->set_rpo_number(kBlockVisited2);
879 // Pop the loop stack and continue visiting outgoing edges within
880 // the context of the outer loop, if any.
881 loop = loop->prev;
882 // We leave the loop header on the stack; the rest of this iteration
883 // and later iterations will go through its outgoing edges list.
884 }
885
886 // Use the next outgoing edge if there are any.
887 size_t outgoing_index = frame->index - block->SuccessorCount();
888 LoopInfo* info = &loops_[GetLoopNumber(block)];
889 DCHECK(loop != info)((void) 0);
890 if (block != entry && info->outgoing != nullptr &&
891 outgoing_index < info->outgoing->size()) {
892 succ = info->outgoing->at(outgoing_index);
893 frame->index++;
894 }
895 }
896
897 if (succ != nullptr) {
898 // Process the next successor.
899 if (succ->rpo_number() == kBlockOnStack) continue;
900 if (succ->rpo_number() == kBlockVisited2) continue;
901 DCHECK_EQ(kBlockUnvisited2, succ->rpo_number())((void) 0);
902 if (loop != nullptr && !loop->members->Contains(succ->id().ToInt())) {
903 // The successor is not in the current loop or any nested loop.
904 // Add it to the outgoing edges of this loop and visit it later.
905 loop->AddOutgoing(zone_, succ);
906 } else {
907 // Push the successor onto the stack.
908 stack_depth = Push(stack_depth, succ, kBlockUnvisited2);
909 if (HasLoopNumber(succ)) {
910 // Push the inner loop onto the loop stack.
911 DCHECK(GetLoopNumber(succ) < num_loops)((void) 0);
912 LoopInfo* next = &loops_[GetLoopNumber(succ)];
913 next->end = order;
914 next->prev = loop;
915 loop = next;
916 }
917 }
918 } else {
919 // Finished with all successors of the current block.
920 if (HasLoopNumber(block)) {
921 // If we are going to pop a loop header, then add its entire body.
922 LoopInfo* info = &loops_[GetLoopNumber(block)];
923 for (BasicBlock* b = info->start; true; b = b->rpo_next()) {
924 if (b->rpo_next() == info->end) {
925 b->set_rpo_next(order);
926 info->end = order;
927 break;
928 }
929 }
930 order = info->start;
931 } else {
932 // Pop a single node off the stack and add it to the order.
933 order = PushFront(order, block);
934 block->set_rpo_number(kBlockVisited2);
935 }
936 stack_depth--;
937 }
938 }
939 }
940
941 // Publish new order the first time.
942 if (order_ == nullptr) order_ = order;
943
944 // Compute the correct loop headers and set the correct loop ends.
945 LoopInfo* current_loop = nullptr;
946 BasicBlock* current_header = entry->loop_header();
947 int32_t loop_depth = entry->loop_depth();
948 if (entry->IsLoopHeader()) --loop_depth; // Entry might be a loop header.
949 for (BasicBlock* b = order; b != insertion_point; b = b->rpo_next()) {
950 BasicBlock* current = b;
951
952 // Reset BasicBlock::rpo_number again.
953 current->set_rpo_number(kBlockUnvisited1);
954
955 // Finish the previous loop(s) if we just exited them.
956 while (current_header != nullptr &&
957 current == current_header->loop_end()) {
958 DCHECK(current_header->IsLoopHeader())((void) 0);
959 DCHECK_NOT_NULL(current_loop)((void) 0);
960 current_loop = current_loop->prev;
961 current_header =
962 current_loop == nullptr ? nullptr : current_loop->header;
963 --loop_depth;
964 }
965 current->set_loop_header(current_header);
966
967 // Push a new loop onto the stack if this loop is a loop header.
968 if (HasLoopNumber(current)) {
969 ++loop_depth;
970 current_loop = &loops_[GetLoopNumber(current)];
971 BasicBlock* loop_end = current_loop->end;
972 current->set_loop_end(loop_end == nullptr ? BeyondEndSentinel()
973 : loop_end);
974 current_header = current_loop->header;
975 TRACE("id:%d is a loop header, increment loop depth to %d\n",
976 current->id().ToInt(), loop_depth);
977 }
978
979 current->set_loop_depth(loop_depth);
980
981 if (current->loop_header() == nullptr) {
982 TRACE("id:%d is not in a loop (depth == %d)\n", current->id().ToInt(),
983 current->loop_depth());
984 } else {
985 TRACE("id:%d has loop header id:%d, (depth == %d)\n",
986 current->id().ToInt(), current->loop_header()->id().ToInt(),
987 current->loop_depth());
988 }
989 }
990 }
991
992 // Computes loop membership from the backedges of the control flow graph.
993 void ComputeLoopInfo(ZoneVector<SpecialRPOStackFrame>* queue,
994 size_t num_loops, ZoneVector<Backedge>* backedges) {
995 // Extend existing loop membership vectors.
996 for (LoopInfo& loop : loops_) {
997 loop.members->Resize(static_cast<int>(schedule_->BasicBlockCount()),
998 zone_);
999 }
1000
1001 // Extend loop information vector.
1002 loops_.resize(num_loops, LoopInfo());
1003
1004 // Compute loop membership starting from backedges.
1005 // O(max(loop_depth) * max(|loop|)
1006 for (size_t i = 0; i < backedges->size(); i++) {
1007 BasicBlock* member = backedges->at(i).first;
1008 BasicBlock* header = member->SuccessorAt(backedges->at(i).second);
1009 size_t loop_num = GetLoopNumber(header);
1010 if (loops_[loop_num].header == nullptr) {
1011 loops_[loop_num].header = header;
1012 loops_[loop_num].members = zone_->New<BitVector>(
1013 static_cast<int>(schedule_->BasicBlockCount()), zone_);
1014 }
1015
1016 int queue_length = 0;
1017 if (member != header) {
1018 // As long as the header doesn't have a backedge to itself,
1019 // Push the member onto the queue and process its predecessors.
1020 if (!loops_[loop_num].members->Contains(member->id().ToInt())) {
1021 loops_[loop_num].members->Add(member->id().ToInt());
1022 }
1023 (*queue)[queue_length++].block = member;
1024 }
1025
1026 // Propagate loop membership backwards. All predecessors of M up to the
1027 // loop header H are members of the loop too. O(|blocks between M and H|).
1028 while (queue_length > 0) {
1029 BasicBlock* block = (*queue)[--queue_length].block;
1030 for (size_t j = 0; j < block->PredecessorCount(); j++) {
1031 BasicBlock* pred = block->PredecessorAt(j);
1032 if (pred != header) {
1033 if (!loops_[loop_num].members->Contains(pred->id().ToInt())) {
1034 loops_[loop_num].members->Add(pred->id().ToInt());
1035 (*queue)[queue_length++].block = pred;
1036 }
1037 }
1038 }
1039 }
1040 }
1041 }
1042
1043#if DEBUG
1044 void PrintRPO() {
1045 StdoutStream os;
1046 os << "RPO with " << loops_.size() << " loops";
1047 if (loops_.size() > 0) {
1048 os << " (";
1049 for (size_t i = 0; i < loops_.size(); i++) {
1050 if (i > 0) os << " ";
1051 os << "id:" << loops_[i].header->id();
1052 }
1053 os << ")";
1054 }
1055 os << ":\n";
1056
1057 for (BasicBlock* block = order_; block != nullptr;
1058 block = block->rpo_next()) {
1059 os << std::setw(5) << "B" << block->rpo_number() << ":";
1060 for (size_t i = 0; i < loops_.size(); i++) {
1061 bool range = loops_[i].header->LoopContains(block);
1062 bool membership = loops_[i].header != block && range;
1063 os << (membership ? " |" : " ");
1064 os << (range ? "x" : " ");
1065 }
1066 os << " id:" << block->id() << ": ";
1067 if (block->loop_end() != nullptr) {
1068 os << " range: [B" << block->rpo_number() << ", B"
1069 << block->loop_end()->rpo_number() << ")";
1070 }
1071 if (block->loop_header() != nullptr) {
1072 os << " header: id:" << block->loop_header()->id();
1073 }
1074 if (block->loop_depth() > 0) {
1075 os << " depth: " << block->loop_depth();
1076 }
1077 os << "\n";
1078 }
1079 }
1080
1081 void VerifySpecialRPO() {
1082 BasicBlockVector* order = schedule_->rpo_order();
1083 DCHECK_LT(0, order->size())((void) 0);
1084 DCHECK_EQ(0, (*order)[0]->id().ToInt())((void) 0); // entry should be first.
1085
1086 for (size_t i = 0; i < loops_.size(); i++) {
1087 LoopInfo* loop = &loops_[i];
1088 BasicBlock* header = loop->header;
1089 BasicBlock* end = header->loop_end();
1090
1091 DCHECK_NOT_NULL(header)((void) 0);
1092 DCHECK_LE(0, header->rpo_number())((void) 0);
1093 DCHECK_LT(header->rpo_number(), order->size())((void) 0);
1094 DCHECK_NOT_NULL(end)((void) 0);
1095 DCHECK_LE(end->rpo_number(), order->size())((void) 0);
1096 DCHECK_GT(end->rpo_number(), header->rpo_number())((void) 0);
1097 DCHECK_NE(header->loop_header(), header)((void) 0);
1098
1099 // Verify the start ... end list relationship.
1100 int links = 0;
1101 BasicBlock* block = loop->start;
1102 DCHECK_EQ(header, block)((void) 0);
1103 bool end_found;
1104 while (true) {
1105 if (block == nullptr || block == loop->end) {
1106 end_found = (loop->end == block);
1107 break;
1108 }
1109 // The list should be in same order as the final result.
1110 DCHECK(block->rpo_number() == links + header->rpo_number())((void) 0);
1111 links++;
1112 block = block->rpo_next();
1113 DCHECK_LT(links, static_cast<int>(2 * order->size()))((void) 0); // cycle?
1114 }
1115 DCHECK_LT(0, links)((void) 0);
1116 DCHECK_EQ(links, end->rpo_number() - header->rpo_number())((void) 0);
1117 DCHECK(end_found)((void) 0);
1118
1119 // Check loop depth of the header.
1120 int loop_depth = 0;
1121 for (LoopInfo* outer = loop; outer != nullptr; outer = outer->prev) {
1122 loop_depth++;
1123 }
1124 DCHECK_EQ(loop_depth, header->loop_depth())((void) 0);
1125
1126 // Check the contiguousness of loops.
1127 int count = 0;
1128 for (int j = 0; j < static_cast<int>(order->size()); j++) {
1129 block = order->at(j);
1130 DCHECK_EQ(block->rpo_number(), j)((void) 0);
1131 if (j < header->rpo_number() || j >= end->rpo_number()) {
1132 DCHECK(!header->LoopContains(block))((void) 0);
1133 } else {
1134 DCHECK(header->LoopContains(block))((void) 0);
1135 DCHECK_GE(block->loop_depth(), loop_depth)((void) 0);
1136 count++;
1137 }
1138 }
1139 DCHECK_EQ(links, count)((void) 0);
1140 }
1141 }
1142#endif // DEBUG
1143
1144 Zone* zone_;
1145 Schedule* schedule_;
1146 BasicBlock* order_;
1147 BasicBlock* beyond_end_;
1148 ZoneVector<LoopInfo> loops_;
1149 ZoneVector<Backedge> backedges_;
1150 ZoneVector<SpecialRPOStackFrame> stack_;
1151 size_t previous_block_count_;
1152 ZoneVector<BasicBlock*> const empty_;
1153};
1154
1155
1156BasicBlockVector* Scheduler::ComputeSpecialRPO(Zone* zone, Schedule* schedule) {
1157 SpecialRPONumberer numberer(zone, schedule);
1158 numberer.ComputeSpecialRPO();
1159 numberer.SerializeRPOIntoSchedule();
1160 numberer.PrintAndVerifySpecialRPO();
1161 return schedule->rpo_order();
1162}
1163
1164
1165void Scheduler::ComputeSpecialRPONumbering() {
1166 TRACE("--- COMPUTING SPECIAL RPO ----------------------------------\n");
1167
1168 // Compute the special reverse-post-order for basic blocks.
1169 special_rpo_ = zone_->New<SpecialRPONumberer>(zone_, schedule_);
1170 special_rpo_->ComputeSpecialRPO();
1171}
1172
1173BasicBlock* Scheduler::GetCommonDominatorIfCached(BasicBlock* b1,
1174 BasicBlock* b2) {
1175 auto entry1 = common_dominator_cache_.find(b1->id().ToInt());
1176 if (entry1 == common_dominator_cache_.end()) return nullptr;
1177 auto entry2 = entry1->second->find(b2->id().ToInt());
1178 if (entry2 == entry1->second->end()) return nullptr;
1179 return entry2->second;
1180}
1181
1182BasicBlock* Scheduler::GetCommonDominator(BasicBlock* b1, BasicBlock* b2) {
1183 // A very common fast case:
1184 if (b1 == b2) return b1;
1185 // Try to find the common dominator by walking, if there is a chance of
1186 // finding it quickly.
1187 constexpr int kCacheGranularity = 63;
1188 STATIC_ASSERT((kCacheGranularity & (kCacheGranularity + 1)) == 0)static_assert((kCacheGranularity & (kCacheGranularity + 1
)) == 0, "(kCacheGranularity & (kCacheGranularity + 1)) == 0"
)
;
1189 int depth_difference = b1->dominator_depth() - b2->dominator_depth();
1190 if (depth_difference > -kCacheGranularity &&
1191 depth_difference < kCacheGranularity) {
1192 for (int i = 0; i < kCacheGranularity; i++) {
1193 if (b1->dominator_depth() < b2->dominator_depth()) {
1194 b2 = b2->dominator();
1195 } else {
1196 b1 = b1->dominator();
1197 }
1198 if (b1 == b2) return b1;
1199 }
1200 // We might fall out of the loop here if the dominator tree has several
1201 // deep "parallel" subtrees.
1202 }
1203 // If it'd be a long walk, take the bus instead (i.e. use the cache).
1204 // To keep memory consumption low, there'll be a bus stop every 64 blocks.
1205 // First, walk to the nearest bus stop.
1206 if (b1->dominator_depth() < b2->dominator_depth()) std::swap(b1, b2);
1207 while ((b1->dominator_depth() & kCacheGranularity) != 0) {
1208 if (V8_LIKELY(b1->dominator_depth() > b2->dominator_depth())(__builtin_expect(!!(b1->dominator_depth() > b2->dominator_depth
()), 1))
) {
1209 b1 = b1->dominator();
1210 } else {
1211 b2 = b2->dominator();
1212 }
1213 if (b1 == b2) return b1;
1214 }
1215 // Then, walk from bus stop to bus stop until we either find a bus (i.e. an
1216 // existing cache entry) or the result. Make a list of any empty bus stops
1217 // we'd like to populate for next time.
1218 constexpr int kMaxNewCacheEntries = 2 * 50; // Must be even.
1219 // This array stores a flattened list of pairs, e.g. if after finding the
1220 // {result}, we want to cache [(B11, B12) -> result, (B21, B22) -> result],
1221 // then we store [11, 12, 21, 22] here.
1222 int new_cache_entries[kMaxNewCacheEntries];
1223 // Next free slot in {new_cache_entries}.
1224 int new_cache_entries_cursor = 0;
1225 while (b1 != b2) {
1226 if ((b1->dominator_depth() & kCacheGranularity) == 0) {
1227 BasicBlock* maybe_cache_hit = GetCommonDominatorIfCached(b1, b2);
1228 if (maybe_cache_hit != nullptr) {
1229 b1 = b2 = maybe_cache_hit;
1230 break;
1231 } else if (new_cache_entries_cursor < kMaxNewCacheEntries) {
1232 new_cache_entries[new_cache_entries_cursor++] = b1->id().ToInt();
1233 new_cache_entries[new_cache_entries_cursor++] = b2->id().ToInt();
1234 }
1235 }
1236 if (V8_LIKELY(b1->dominator_depth() > b2->dominator_depth())(__builtin_expect(!!(b1->dominator_depth() > b2->dominator_depth
()), 1))
) {
1237 b1 = b1->dominator();
1238 } else {
1239 b2 = b2->dominator();
1240 }
1241 }
1242 // Lastly, create new cache entries we noted down earlier.
1243 BasicBlock* result = b1;
1244 for (int i = 0; i < new_cache_entries_cursor;) {
1245 int id1 = new_cache_entries[i++];
1246 int id2 = new_cache_entries[i++];
1247 ZoneMap<int, BasicBlock*>* mapping;
1248 auto entry = common_dominator_cache_.find(id1);
1249 if (entry == common_dominator_cache_.end()) {
1250 mapping = zone_->New<ZoneMap<int, BasicBlock*>>(zone_);
1251 common_dominator_cache_[id1] = mapping;
1252 } else {
1253 mapping = entry->second;
1254 }
1255 // If there was an existing entry, we would have found it earlier.
1256 DCHECK_EQ(mapping->find(id2), mapping->end())((void) 0);
1257 mapping->insert({id2, result});
1258 }
1259 return result;
1260}
1261
1262void Scheduler::PropagateImmediateDominators(BasicBlock* block) {
1263 for (/*nop*/; block != nullptr; block = block->rpo_next()) {
1264 auto pred = block->predecessors().begin();
1265 auto end = block->predecessors().end();
1266 DCHECK(pred != end)((void) 0); // All blocks except start have predecessors.
1267 BasicBlock* dominator = *pred;
1268 bool deferred = dominator->deferred();
1269 // For multiple predecessors, walk up the dominator tree until a common
1270 // dominator is found. Visitation order guarantees that all predecessors
1271 // except for backwards edges have been visited.
1272 // We use a one-element cache for previously-seen dominators. This gets
1273 // hit a lot for functions that have long chains of diamonds, and in
1274 // those cases turns quadratic into linear complexity.
1275 BasicBlock* cache = nullptr;
1276 for (++pred; pred != end; ++pred) {
1277 // Don't examine backwards edges.
1278 if ((*pred)->dominator_depth() < 0) continue;
1279 if ((*pred)->dominator_depth() > 3 &&
1280 ((*pred)->dominator()->dominator() == cache ||
1281 (*pred)->dominator()->dominator()->dominator() == cache)) {
1282 // Nothing to do, the last iteration covered this case.
1283 DCHECK_EQ(dominator, BasicBlock::GetCommonDominator(dominator, *pred))((void) 0);
1284 } else {
1285 dominator = BasicBlock::GetCommonDominator(dominator, *pred);
1286 }
1287 cache = (*pred)->dominator();
1288 deferred = deferred & (*pred)->deferred();
1289 }
1290 block->set_dominator(dominator);
1291 block->set_dominator_depth(dominator->dominator_depth() + 1);
1292 block->set_deferred(deferred | block->deferred());
1293 TRACE("Block id:%d's idom is id:%d, depth = %d\n", block->id().ToInt(),
1294 dominator->id().ToInt(), block->dominator_depth());
1295 }
1296}
1297
1298void Scheduler::GenerateDominatorTree(Schedule* schedule) {
1299 // Seed start block to be the first dominator.
1300 schedule->start()->set_dominator_depth(0);
1301
1302 // Build the block dominator tree resulting from the above seed.
1303 PropagateImmediateDominators(schedule->start()->rpo_next());
1304}
1305
1306void Scheduler::GenerateDominatorTree() {
1307 TRACE("--- IMMEDIATE BLOCK DOMINATORS -----------------------------\n");
1308 GenerateDominatorTree(schedule_);
1309}
1310
1311// -----------------------------------------------------------------------------
1312// Phase 3: Prepare use counts for nodes.
1313
1314
1315class PrepareUsesVisitor {
1316 public:
1317 explicit PrepareUsesVisitor(Scheduler* scheduler, Graph* graph, Zone* zone)
1318 : scheduler_(scheduler),
1319 schedule_(scheduler->schedule_),
1320 graph_(graph),
1321 visited_(graph_->NodeCount(), false, zone),
1322 stack_(zone) {}
1323
1324 void Run() {
1325 InitializePlacement(graph_->end());
1326 while (!stack_.empty()) {
1327 Node* node = stack_.top();
1328 stack_.pop();
1329 VisitInputs(node);
1330 }
1331 }
1332
1333 private:
1334 void InitializePlacement(Node* node) {
1335 TRACE("Pre #%d:%s\n", node->id(), node->op()->mnemonic());
1336 DCHECK(!Visited(node))((void) 0);
1337 if (scheduler_->InitializePlacement(node) == Scheduler::kFixed) {
1338 // Fixed nodes are always roots for schedule late.
1339 scheduler_->schedule_root_nodes_.push_back(node);
1340 if (!schedule_->IsScheduled(node)) {
1341 // Make sure root nodes are scheduled in their respective blocks.
1342 TRACE("Scheduling fixed position node #%d:%s\n", node->id(),
1343 node->op()->mnemonic());
1344 IrOpcode::Value opcode = node->opcode();
1345 BasicBlock* block =
1346 opcode == IrOpcode::kParameter
1347 ? schedule_->start()
1348 : schedule_->block(NodeProperties::GetControlInput(node));
1349 DCHECK_NOT_NULL(block)((void) 0);
1350 schedule_->AddNode(block, node);
1351 }
1352 }
1353 stack_.push(node);
1354 visited_[node->id()] = true;
1355 }
1356
1357 void VisitInputs(Node* node) {
1358 DCHECK_NE(scheduler_->GetPlacement(node), Scheduler::kUnknown)((void) 0);
1359 bool is_scheduled = schedule_->IsScheduled(node);
1360 base::Optional<int> coupled_control_edge =
1361 scheduler_->GetCoupledControlEdge(node);
1362 for (auto edge : node->input_edges()) {
1363 Node* to = edge.to();
1364 DCHECK_EQ(node, edge.from())((void) 0);
1365 if (!Visited(to)) {
1366 InitializePlacement(to);
1367 }
1368 TRACE("PostEdge #%d:%s->#%d:%s\n", node->id(), node->op()->mnemonic(),
1369 to->id(), to->op()->mnemonic());
1370 DCHECK_NE(scheduler_->GetPlacement(to), Scheduler::kUnknown)((void) 0);
1371 if (!is_scheduled && edge.index() != coupled_control_edge) {
1372 scheduler_->IncrementUnscheduledUseCount(to, node);
1373 }
1374 }
1375 }
1376
1377 bool Visited(Node* node) { return visited_[node->id()]; }
1378
1379 Scheduler* scheduler_;
1380 Schedule* schedule_;
1381 Graph* graph_;
1382 BoolVector visited_;
1383 ZoneStack<Node*> stack_;
1384};
1385
1386
1387void Scheduler::PrepareUses() {
1388 TRACE("--- PREPARE USES -------------------------------------------\n");
1389
1390 // Count the uses of every node, which is used to ensure that all of a
1391 // node's uses are scheduled before the node itself.
1392 PrepareUsesVisitor prepare_uses(this, graph_, zone_);
1393 prepare_uses.Run();
1394}
1395
1396
1397// -----------------------------------------------------------------------------
1398// Phase 4: Schedule nodes early.
1399
1400
1401class ScheduleEarlyNodeVisitor {
1402 public:
1403 ScheduleEarlyNodeVisitor(Zone* zone, Scheduler* scheduler)
1404 : scheduler_(scheduler), schedule_(scheduler->schedule_), queue_(zone) {}
1405
1406 // Run the schedule early algorithm on a set of fixed root nodes.
1407 void Run(NodeVector* roots) {
1408 for (Node* const root : *roots) {
1409 queue_.push(root);
1410 }
1411
1412 while (!queue_.empty()) {
1413 scheduler_->tick_counter_->TickAndMaybeEnterSafepoint();
1414 VisitNode(queue_.front());
1415 queue_.pop();
1416 }
1417 }
1418
1419 private:
1420 // Visits one node from the queue and propagates its current schedule early
1421 // position to all uses. This in turn might push more nodes onto the queue.
1422 void VisitNode(Node* node) {
1423 Scheduler::SchedulerData* data = scheduler_->GetData(node);
1424
1425 // Fixed nodes already know their schedule early position.
1426 if (scheduler_->GetPlacement(node) == Scheduler::kFixed) {
1427 data->minimum_block_ = schedule_->block(node);
1428 TRACE("Fixing #%d:%s minimum_block = id:%d, dominator_depth = %d\n",
1429 node->id(), node->op()->mnemonic(),
1430 data->minimum_block_->id().ToInt(),
1431 data->minimum_block_->dominator_depth());
1432 }
1433
1434 // No need to propagate unconstrained schedule early positions.
1435 if (data->minimum_block_ == schedule_->start()) return;
1436
1437 // Propagate schedule early position.
1438 DCHECK_NOT_NULL(data->minimum_block_)((void) 0);
1439 for (auto use : node->uses()) {
1440 if (scheduler_->IsLive(use)) {
1441 PropagateMinimumPositionToNode(data->minimum_block_, use);
1442 }
1443 }
1444 }
1445
1446 // Propagates {block} as another minimum position into the given {node}. This
1447 // has the net effect of computing the minimum dominator block of {node} that
1448 // still post-dominates all inputs to {node} when the queue is processed.
1449 void PropagateMinimumPositionToNode(BasicBlock* block, Node* node) {
1450 Scheduler::SchedulerData* data = scheduler_->GetData(node);
1451
1452 // No need to propagate to fixed node, it's guaranteed to be a root.
1453 if (scheduler_->GetPlacement(node) == Scheduler::kFixed) return;
1454
1455 // Coupled nodes influence schedule early position of their control.
1456 if (scheduler_->GetPlacement(node) == Scheduler::kCoupled) {
1457 Node* control = NodeProperties::GetControlInput(node);
1458 PropagateMinimumPositionToNode(block, control);
1459 }
1460
1461 // Propagate new position if it is deeper down the dominator tree than the
1462 // current. Note that all inputs need to have minimum block position inside
1463 // the dominator chain of {node}'s minimum block position.
1464 DCHECK(InsideSameDominatorChain(block, data->minimum_block_))((void) 0);
1465 if (block->dominator_depth() > data->minimum_block_->dominator_depth()) {
1466 data->minimum_block_ = block;
1467 queue_.push(node);
1468 TRACE("Propagating #%d:%s minimum_block = id:%d, dominator_depth = %d\n",
1469 node->id(), node->op()->mnemonic(),
1470 data->minimum_block_->id().ToInt(),
1471 data->minimum_block_->dominator_depth());
1472 }
1473 }
1474
1475#if DEBUG
1476 bool InsideSameDominatorChain(BasicBlock* b1, BasicBlock* b2) {
1477 BasicBlock* dominator = BasicBlock::GetCommonDominator(b1, b2);
1478 return dominator == b1 || dominator == b2;
1479 }
1480#endif
1481
1482 Scheduler* scheduler_;
1483 Schedule* schedule_;
1484 ZoneQueue<Node*> queue_;
1485};
1486
1487
1488void Scheduler::ScheduleEarly() {
1489 if (!special_rpo_->HasLoopBlocks()) {
1490 TRACE("--- NO LOOPS SO SKIPPING SCHEDULE EARLY --------------------\n");
1491 return;
1492 }
1493
1494 TRACE("--- SCHEDULE EARLY -----------------------------------------\n");
1495 if (FLAG_trace_turbo_scheduler) {
1496 TRACE("roots: ");
1497 for (Node* node : schedule_root_nodes_) {
1498 TRACE("#%d:%s ", node->id(), node->op()->mnemonic());
1499 }
1500 TRACE("\n");
1501 }
1502
1503 // Compute the minimum block for each node thereby determining the earliest
1504 // position each node could be placed within a valid schedule.
1505 ScheduleEarlyNodeVisitor schedule_early_visitor(zone_, this);
1506 schedule_early_visitor.Run(&schedule_root_nodes_);
1507}
1508
1509
1510// -----------------------------------------------------------------------------
1511// Phase 5: Schedule nodes late.
1512
1513
1514class ScheduleLateNodeVisitor {
1515 public:
1516 ScheduleLateNodeVisitor(Zone* zone, Scheduler* scheduler)
1517 : zone_(zone),
1518 scheduler_(scheduler),
1519 schedule_(scheduler_->schedule_),
1520 marked_(scheduler->zone_),
1521 marking_queue_(scheduler->zone_) {}
1522
1523 // Run the schedule late algorithm on a set of fixed root nodes.
1524 void Run(NodeVector* roots) {
1525 for (Node* const root : *roots) {
1526 ProcessQueue(root);
1527 }
1528 }
1529
1530 private:
1531 void ProcessQueue(Node* root) {
1532 ZoneQueue<Node*>* queue = &(scheduler_->schedule_queue_);
1533 for (Node* node : root->inputs()) {
1534 // Don't schedule coupled nodes on their own.
1535 if (scheduler_->GetPlacement(node) == Scheduler::kCoupled) {
1536 node = NodeProperties::GetControlInput(node);
1537 }
1538
1539 // Test schedulability condition by looking at unscheduled use count.
1540 if (scheduler_->GetData(node)->unscheduled_count_ != 0) continue;
1541
1542 queue->push(node);
1543 do {
1544 scheduler_->tick_counter_->TickAndMaybeEnterSafepoint();
1545 Node* const n = queue->front();
1546 queue->pop();
1547 VisitNode(n);
1548 } while (!queue->empty());
1549 }
1550 }
1551
1552 // Visits one node from the queue of schedulable nodes and determines its
1553 // schedule late position. Also hoists nodes out of loops to find a more
1554 // optimal scheduling position.
1555 void VisitNode(Node* node) {
1556 DCHECK_EQ(0, scheduler_->GetData(node)->unscheduled_count_)((void) 0);
1557
1558 // Don't schedule nodes that are already scheduled.
1559 if (schedule_->IsScheduled(node)) return;
1560 DCHECK_EQ(Scheduler::kSchedulable, scheduler_->GetPlacement(node))((void) 0);
1561
1562 // Determine the dominating block for all of the uses of this node. It is
1563 // the latest block that this node can be scheduled in.
1564 TRACE("Scheduling #%d:%s\n", node->id(), node->op()->mnemonic());
1565 BasicBlock* block = GetCommonDominatorOfUses(node);
1566 DCHECK_NOT_NULL(block)((void) 0);
1567
1568 // The schedule early block dominates the schedule late block.
1569 BasicBlock* min_block = scheduler_->GetData(node)->minimum_block_;
1570 DCHECK_EQ(min_block, BasicBlock::GetCommonDominator(block, min_block))((void) 0);
1571
1572 TRACE(
1573 "Schedule late of #%d:%s is id:%d at loop depth %d, minimum = id:%d\n",
1574 node->id(), node->op()->mnemonic(), block->id().ToInt(),
1575 block->loop_depth(), min_block->id().ToInt());
1576
1577 // Hoist nodes out of loops if possible. Nodes can be hoisted iteratively
1578 // into enclosing loop pre-headers until they would precede their schedule
1579 // early position.
1580 BasicBlock* hoist_block = GetHoistBlock(block);
1581 if (hoist_block &&
1582 hoist_block->dominator_depth() >= min_block->dominator_depth()) {
1583 DCHECK(scheduler_->special_rpo_->HasLoopBlocks())((void) 0);
1584 do {
1585 TRACE(" hoisting #%d:%s to block id:%d\n", node->id(),
1586 node->op()->mnemonic(), hoist_block->id().ToInt());
1587 DCHECK_LT(hoist_block->loop_depth(), block->loop_depth())((void) 0);
1588 block = hoist_block;
1589 hoist_block = GetHoistBlock(hoist_block);
1590 } while (hoist_block &&
1591 hoist_block->dominator_depth() >= min_block->dominator_depth());
1592 } else if (scheduler_->flags_ & Scheduler::kSplitNodes) {
1593 // Split the {node} if beneficial and return the new {block} for it.
1594 block = SplitNode(block, node);
1595 }
1596
1597 // Schedule the node or a floating control structure.
1598 if (IrOpcode::IsMergeOpcode(node->opcode())) {
1599 ScheduleFloatingControl(block, node);
1600 } else if (node->opcode() == IrOpcode::kFinishRegion) {
1601 ScheduleRegion(block, node);
1602 } else {
1603 ScheduleNode(block, node);
1604 }
1605 }
1606
1607 bool IsMarked(BasicBlock* block) const {
1608 DCHECK_LT(block->id().ToSize(), marked_.size())((void) 0);
1609 return marked_[block->id().ToSize()];
1610 }
1611
1612 void Mark(BasicBlock* block) { marked_[block->id().ToSize()] = true; }
1613
1614 // Mark {block} and push its non-marked predecessor on the marking queue.
1615 void MarkBlock(BasicBlock* block) {
1616 DCHECK_LT(block->id().ToSize(), marked_.size())((void) 0);
1617 Mark(block);
1618 for (BasicBlock* pred_block : block->predecessors()) {
1619 if (IsMarked(pred_block)) continue;
1620 marking_queue_.push_back(pred_block);
1621 }
1622 }
1623
1624 BasicBlock* SplitNode(BasicBlock* block, Node* node) {
1625 // For now, we limit splitting to pure nodes.
1626 if (!node->op()->HasProperty(Operator::kPure)) return block;
1627 // TODO(titzer): fix the special case of splitting of projections.
1628 if (node->opcode() == IrOpcode::kProjection) return block;
1629
1630 // The {block} is common dominator of all uses of {node}, so we cannot
1631 // split anything unless the {block} has at least two successors.
1632 DCHECK_EQ(block, GetCommonDominatorOfUses(node))((void) 0);
1633 if (block->SuccessorCount() < 2) return block;
1634
1635 // Clear marking bits.
1636 DCHECK(marking_queue_.empty())((void) 0);
1637 std::fill(marked_.begin(), marked_.end(), false);
1638 marked_.resize(schedule_->BasicBlockCount() + 1, false);
1639
1640 // Check if the {node} has uses in {block}.
1641 for (Edge edge : node->use_edges()) {
1642 if (!scheduler_->IsLive(edge.from())) continue;
1643 BasicBlock* use_block = GetBlockForUse(edge);
1644 if (use_block == nullptr || IsMarked(use_block)) continue;
1645 if (use_block == block) {
1646 TRACE(" not splitting #%d:%s, it is used in id:%d\n", node->id(),
1647 node->op()->mnemonic(), block->id().ToInt());
1648 marking_queue_.clear();
1649 return block;
1650 }
1651 MarkBlock(use_block);
1652 }
1653
1654 // Compute transitive marking closure; a block is marked if all its
1655 // successors are marked.
1656 do {
1657 BasicBlock* top_block = marking_queue_.front();
1658 marking_queue_.pop_front();
1659 if (IsMarked(top_block)) continue;
1660 bool marked = true;
1661 for (BasicBlock* successor : top_block->successors()) {
1662 if (!IsMarked(successor)) {
1663 marked = false;
1664 break;
1665 }
1666 }
1667 if (marked) MarkBlock(top_block);
1668 } while (!marking_queue_.empty());
1669
1670 // If the (common dominator) {block} is marked, we know that all paths from
1671 // {block} to the end contain at least one use of {node}, and hence there's
1672 // no point in splitting the {node} in this case.
1673 if (IsMarked(block)) {
1674 TRACE(" not splitting #%d:%s, its common dominator id:%d is perfect\n",
1675 node->id(), node->op()->mnemonic(), block->id().ToInt());
1676 return block;
1677 }
1678
1679 // Split {node} for uses according to the previously computed marking
1680 // closure. Every marking partition has a unique dominator, which get's a
1681 // copy of the {node} with the exception of the first partition, which get's
1682 // the {node} itself.
1683 ZoneMap<BasicBlock*, Node*> dominators(scheduler_->zone_);
1684 for (Edge edge : node->use_edges()) {
1685 if (!scheduler_->IsLive(edge.from())) continue;
1686 BasicBlock* use_block = GetBlockForUse(edge);
1687 if (use_block == nullptr) continue;
1688 while (IsMarked(use_block->dominator())) {
1689 use_block = use_block->dominator();
1690 }
1691 auto& use_node = dominators[use_block];
1692 if (use_node == nullptr) {
1693 if (dominators.size() == 1u) {
1694 // Place the {node} at {use_block}.
1695 block = use_block;
1696 use_node = node;
1697 TRACE(" pushing #%d:%s down to id:%d\n", node->id(),
1698 node->op()->mnemonic(), block->id().ToInt());
1699 } else {
1700 // Place a copy of {node} at {use_block}.
1701 use_node = CloneNode(node);
1702 TRACE(" cloning #%d:%s for id:%d\n", use_node->id(),
1703 use_node->op()->mnemonic(), use_block->id().ToInt());
1704 scheduler_->schedule_queue_.push(use_node);
1705 }
1706 }
1707 edge.UpdateTo(use_node);
1708 }
1709 return block;
1710 }
1711
1712 BasicBlock* GetHoistBlock(BasicBlock* block) {
1713 if (!scheduler_->special_rpo_->HasLoopBlocks()) return nullptr;
1714 if (block->IsLoopHeader()) return block->dominator();
1715 // We have to check to make sure that the {block} dominates all
1716 // of the outgoing blocks. If it doesn't, then there is a path
1717 // out of the loop which does not execute this {block}, so we
1718 // can't hoist operations from this {block} out of the loop, as
1719 // that would introduce additional computations.
1720 if (BasicBlock* header_block = block->loop_header()) {
1721 for (BasicBlock* outgoing_block :
1722 scheduler_->special_rpo_->GetOutgoingBlocks(header_block)) {
1723 if (scheduler_->GetCommonDominator(block, outgoing_block) != block) {
1724 return nullptr;
1725 }
1726 }
1727 return header_block->dominator();
1728 }
1729 return nullptr;
1730 }
1731
1732 BasicBlock* GetCommonDominatorOfUses(Node* node) {
1733 BasicBlock* block = nullptr;
1734 for (Edge edge : node->use_edges()) {
1735 if (!scheduler_->IsLive(edge.from())) continue;
1736 BasicBlock* use_block = GetBlockForUse(edge);
1737 block = block == nullptr
1738 ? use_block
1739 : use_block == nullptr
1740 ? block
1741 : scheduler_->GetCommonDominator(block, use_block);
1742 }
1743 return block;
1744 }
1745
1746 BasicBlock* FindPredecessorBlock(Node* node) {
1747 return scheduler_->control_flow_builder_->FindPredecessorBlock(node);
1748 }
1749
1750 BasicBlock* GetBlockForUse(Edge edge) {
1751 // TODO(titzer): ignore uses from dead nodes (not visited in PrepareUses()).
1752 // Dead uses only occur if the graph is not trimmed before scheduling.
1753 Node* use = edge.from();
1754 if (IrOpcode::IsPhiOpcode(use->opcode())) {
1755 // If the use is from a coupled (i.e. floating) phi, compute the common
1756 // dominator of its uses. This will not recurse more than one level.
1757 if (scheduler_->GetPlacement(use) == Scheduler::kCoupled) {
1758 TRACE(" inspecting uses of coupled #%d:%s\n", use->id(),
1759 use->op()->mnemonic());
1760 // TODO(titzer): reenable once above TODO is addressed.
1761 // DCHECK_EQ(edge.to(), NodeProperties::GetControlInput(use));
1762 return GetCommonDominatorOfUses(use);
1763 }
1764 // If the use is from a fixed (i.e. non-floating) phi, we use the
1765 // predecessor block of the corresponding control input to the merge.
1766 if (scheduler_->GetPlacement(use) == Scheduler::kFixed) {
1767 TRACE(" input@%d into a fixed phi #%d:%s\n", edge.index(), use->id(),
1768 use->op()->mnemonic());
1769 Node* merge = NodeProperties::GetControlInput(use, 0);
1770 DCHECK(IrOpcode::IsMergeOpcode(merge->opcode()))((void) 0);
1771 Node* input = NodeProperties::GetControlInput(merge, edge.index());
1772 return FindPredecessorBlock(input);
1773 }
1774 } else if (IrOpcode::IsMergeOpcode(use->opcode())) {
1775 // If the use is from a fixed (i.e. non-floating) merge, we use the
1776 // predecessor block of the current input to the merge.
1777 if (scheduler_->GetPlacement(use) == Scheduler::kFixed) {
1778 TRACE(" input@%d into a fixed merge #%d:%s\n", edge.index(), use->id(),
1779 use->op()->mnemonic());
1780 return FindPredecessorBlock(edge.to());
1781 }
1782 }
1783 BasicBlock* result = schedule_->block(use);
1784 if (result == nullptr) return nullptr;
1785 TRACE(" must dominate use #%d:%s in id:%d\n", use->id(),
1786 use->op()->mnemonic(), result->id().ToInt());
1787 return result;
1788 }
1789
1790 void ScheduleFloatingControl(BasicBlock* block, Node* node) {
1791 scheduler_->FuseFloatingControl(block, node);
1792 }
1793
1794 void ScheduleRegion(BasicBlock* block, Node* region_end) {
1795 // We only allow regions of instructions connected into a linear
1796 // effect chain. The only value allowed to be produced by a node
1797 // in the chain must be the value consumed by the FinishRegion node.
1798
1799 // We schedule back to front; we first schedule FinishRegion.
1800 CHECK_EQ(IrOpcode::kFinishRegion, region_end->opcode())do { bool _cmp = ::v8::base::CmpEQImpl< typename ::v8::base
::pass_value_or_ref<decltype(IrOpcode::kFinishRegion)>::
type, typename ::v8::base::pass_value_or_ref<decltype(region_end
->opcode())>::type>((IrOpcode::kFinishRegion), (region_end
->opcode())); do { if ((__builtin_expect(!!(!(_cmp)), 0)))
{ V8_Fatal("Check failed: %s.", "IrOpcode::kFinishRegion" " "
"==" " " "region_end->opcode()"); } } while (false); } while
(false)
;
1801 ScheduleNode(block, region_end);
1802
1803 // Schedule the chain.
1804 Node* node = NodeProperties::GetEffectInput(region_end);
1805 while (node->opcode() != IrOpcode::kBeginRegion) {
1806 DCHECK_EQ(0, scheduler_->GetData(node)->unscheduled_count_)((void) 0);
1807 DCHECK_EQ(1, node->op()->EffectInputCount())((void) 0);
1808 DCHECK_EQ(1, node->op()->EffectOutputCount())((void) 0);
1809 DCHECK_EQ(0, node->op()->ControlOutputCount())((void) 0);
1810 // The value output (if there is any) must be consumed
1811 // by the EndRegion node.
1812 DCHECK(node->op()->ValueOutputCount() == 0 ||((void) 0)
1813 node == region_end->InputAt(0))((void) 0);
1814 ScheduleNode(block, node);
1815 node = NodeProperties::GetEffectInput(node);
1816 }
1817 // Schedule the BeginRegion node.
1818 DCHECK_EQ(0, scheduler_->GetData(node)->unscheduled_count_)((void) 0);
1819 ScheduleNode(block, node);
1820 }
1821
1822 void ScheduleNode(BasicBlock* block, Node* node) {
1823 schedule_->PlanNode(block, node);
1824 size_t block_id = block->id().ToSize();
1825 if (!scheduler_->scheduled_nodes_[block_id]) {
1826 scheduler_->scheduled_nodes_[block_id] = zone_->New<NodeVector>(zone_);
1827 }
1828 scheduler_->scheduled_nodes_[block_id]->push_back(node);
1829 scheduler_->UpdatePlacement(node, Scheduler::kScheduled);
1830 }
1831
1832 Node* CloneNode(Node* node) {
1833 int const input_count = node->InputCount();
1834 base::Optional<int> coupled_control_edge =
1835 scheduler_->GetCoupledControlEdge(node);
1836 for (int index = 0; index < input_count; ++index) {
1837 if (index != coupled_control_edge) {
1838 Node* const input = node->InputAt(index);
1839 scheduler_->IncrementUnscheduledUseCount(input, node);
1840 }
1841 }
1842 Node* const copy = scheduler_->graph_->CloneNode(node);
1843 TRACE(("clone #%d:%s -> #%d\n"), node->id(), node->op()->mnemonic(),
1844 copy->id());
1845 scheduler_->node_data_.resize(copy->id() + 1,
1846 scheduler_->DefaultSchedulerData());
1847 scheduler_->node_data_[copy->id()] = scheduler_->node_data_[node->id()];
1848 return copy;
1849 }
1850
1851 Zone* zone_;
1852 Scheduler* scheduler_;
1853 Schedule* schedule_;
1854 BoolVector marked_;
1855 ZoneDeque<BasicBlock*> marking_queue_;
1856};
1857
1858
1859void Scheduler::ScheduleLate() {
1860 TRACE("--- SCHEDULE LATE ------------------------------------------\n");
1861 if (FLAG_trace_turbo_scheduler) {
1862 TRACE("roots: ");
1863 for (Node* node : schedule_root_nodes_) {
1864 TRACE("#%d:%s ", node->id(), node->op()->mnemonic());
1865 }
1866 TRACE("\n");
1867 }
1868
1869 // Schedule: Places nodes in dominator block of all their uses.
1870 ScheduleLateNodeVisitor schedule_late_visitor(zone_, this);
1871 schedule_late_visitor.Run(&schedule_root_nodes_);
1872}
1873
1874
1875// -----------------------------------------------------------------------------
1876// Phase 6: Seal the final schedule.
1877
1878
1879void Scheduler::SealFinalSchedule() {
1880 TRACE("--- SEAL FINAL SCHEDULE ------------------------------------\n");
1881
1882 // Serialize the assembly order and reverse-post-order numbering.
1883 special_rpo_->SerializeRPOIntoSchedule();
1884 special_rpo_->PrintAndVerifySpecialRPO();
1885
1886 // Add collected nodes for basic blocks to their blocks in the right order.
1887 int block_num = 0;
1888 for (NodeVector* nodes : scheduled_nodes_) {
1889 BasicBlock::Id id = BasicBlock::Id::FromInt(block_num++);
1890 BasicBlock* block = schedule_->GetBlockById(id);
1891 if (nodes) {
1892 for (Node* node : base::Reversed(*nodes)) {
1893 schedule_->AddNode(block, node);
1894 }
1895 }
1896 }
1897}
1898
1899
1900// -----------------------------------------------------------------------------
1901
1902
1903void Scheduler::FuseFloatingControl(BasicBlock* block, Node* node) {
1904 TRACE("--- FUSE FLOATING CONTROL ----------------------------------\n");
1905 if (FLAG_trace_turbo_scheduler) {
1906 StdoutStream{} << "Schedule before control flow fusion:\n" << *schedule_;
1907 }
1908
1909 // Iterate on phase 1: Build control-flow graph.
1910 control_flow_builder_->Run(block, node);
1911
1912 // Iterate on phase 2: Compute special RPO and dominator tree.
1913 special_rpo_->UpdateSpecialRPO(block, schedule_->block(node));
1914 // TODO(turbofan): Currently "iterate on" means "re-run". Fix that.
1915 for (BasicBlock* b = block->rpo_next(); b != nullptr; b = b->rpo_next()) {
1916 b->set_dominator_depth(-1);
1917 b->set_dominator(nullptr);
1918 }
1919 PropagateImmediateDominators(block->rpo_next());
1920
1921 // Iterate on phase 4: Schedule nodes early.
1922 // TODO(turbofan): The following loop gathering the propagation roots is a
1923 // temporary solution and should be merged into the rest of the scheduler as
1924 // soon as the approach settled for all floating loops.
1925 NodeVector propagation_roots(control_flow_builder_->control_);
1926 for (Node* control : control_flow_builder_->control_) {
1927 for (Node* use : control->uses()) {
1928 if (NodeProperties::IsPhi(use) && IsLive(use)) {
1929 propagation_roots.push_back(use);
1930 }
1931 }
1932 }
1933 if (FLAG_trace_turbo_scheduler) {
1934 TRACE("propagation roots: ");
1935 for (Node* r : propagation_roots) {
1936 TRACE("#%d:%s ", r->id(), r->op()->mnemonic());
1937 }
1938 TRACE("\n");
1939 }
1940 ScheduleEarlyNodeVisitor schedule_early_visitor(zone_, this);
1941 schedule_early_visitor.Run(&propagation_roots);
1942
1943 // Move previously planned nodes.
1944 // TODO(turbofan): Improve that by supporting bulk moves.
1945 scheduled_nodes_.resize(schedule_->BasicBlockCount());
1946 MovePlannedNodes(block, schedule_->block(node));
1947
1948 if (FLAG_trace_turbo_scheduler) {
1949 StdoutStream{} << "Schedule after control flow fusion:\n" << *schedule_;
1950 }
1951}
1952
1953
1954void Scheduler::MovePlannedNodes(BasicBlock* from, BasicBlock* to) {
1955 TRACE("Move planned nodes from id:%d to id:%d\n", from->id().ToInt(),
1956 to->id().ToInt());
1957 NodeVector* from_nodes = scheduled_nodes_[from->id().ToSize()];
1958 NodeVector* to_nodes = scheduled_nodes_[to->id().ToSize()];
1959 if (!from_nodes) return;
1960
1961 for (Node* const node : *from_nodes) {
1962 schedule_->SetBlockForNode(to, node);
1963 }
1964 if (to_nodes) {
1965 to_nodes->insert(to_nodes->end(), from_nodes->begin(), from_nodes->end());
1966 from_nodes->clear();
1967 } else {
1968 std::swap(scheduled_nodes_[from->id().ToSize()],
1969 scheduled_nodes_[to->id().ToSize()]);
1970 }
1971}
1972
1973#undef TRACE
1974
1975} // namespace compiler
1976} // namespace internal
1977} // namespace v8