Bug Summary

File:out/../deps/v8/src/compiler/scheduler.cc
Warning:line 1229, column 14
Although the value stored to 'b2' is used in the enclosing expression, the value is never actually read from 'b2'

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)
;
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)
;
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)
;
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) {
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())) {
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 =
854 HasLoopNumber(entry) ? &loops_[GetLoopNumber(entry)] : nullptr;
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) {
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()) {
868 // Process the next normal successor.
869 succ = block->SuccessorAt(frame->index++);
870 } else if (HasLoopNumber(block)) {
871 // Process additional outgoing edges from the loop header.
872 if (block->rpo_number() == kBlockOnStack) {
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);
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;
Although the value stored to 'b2' is used in the enclosing expression, the value is never actually read from 'b2'
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