Flutter Engine
The Flutter Engine
branch_optimizer.cc
Go to the documentation of this file.
1// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
6
9
10namespace dart {
11
12// Returns true if the given phi has a single input use and
13// is used in the environments either at the corresponding block entry or
14// at the same instruction where input use is.
15static bool PhiHasSingleUse(PhiInstr* phi, Value* use) {
16 if ((use->next_use() != nullptr) || (phi->input_use_list() != use)) {
17 return false;
18 }
19
20 BlockEntryInstr* block = phi->block();
21 for (Value* env_use = phi->env_use_list(); env_use != nullptr;
22 env_use = env_use->next_use()) {
23 if ((env_use->instruction() != block) &&
24 (env_use->instruction() != use->instruction())) {
25 return false;
26 }
27 }
28
29 return true;
30}
31
32bool BranchSimplifier::Match(JoinEntryInstr* block) {
33 // Match the pattern of a branch on a comparison whose left operand is a
34 // phi from the same block, and whose right operand is a constant.
35 //
36 // Branch(Comparison(kind, Phi, Constant))
37 //
38 // These are the branches produced by inlining in a test context. Also,
39 // the phi has no other uses so they can simply be eliminated. The block
40 // has no other phis and no instructions intervening between the phi and
41 // branch so the block can simply be eliminated.
42 BranchInstr* branch = block->last_instruction()->AsBranch();
43 ASSERT(branch != nullptr);
44 ComparisonInstr* comparison = branch->comparison();
45 if (comparison->InputCount() != 2) {
46 return false;
47 }
48 if (comparison->CanDeoptimize() || comparison->MayThrow()) {
49 return false;
50 }
51 Value* left = comparison->left();
52 PhiInstr* phi = left->definition()->AsPhi();
53 Value* right = comparison->right();
54 ConstantInstr* constant =
55 (right == nullptr) ? nullptr : right->definition()->AsConstant();
56 return (phi != nullptr) && (constant != nullptr) &&
57 (phi->GetBlock() == block) && PhiHasSingleUse(phi, left) &&
58 (block->next() == branch) && (block->phis()->length() == 1);
59}
60
63 // Convert a target block into a join block. Branches will be duplicated
64 // so the former true and false targets become joins of the control flows
65 // from all the duplicated branches.
66 JoinEntryInstr* join = new (zone)
67 JoinEntryInstr(target->block_id(), target->try_index(), DeoptId::kNone);
68 join->InheritDeoptTarget(zone, target);
69 join->LinkTo(target->next());
70 join->set_last_instruction(target->last_instruction());
71 target->UnuseAllInputs();
72 return join;
73}
74
77 auto replacement = new (zone)
78 TargetEntryInstr(target->block_id(), target->try_index(), DeoptId::kNone);
79 replacement->InheritDeoptTarget(zone, target);
80 replacement->LinkTo(target->next());
81 replacement->set_last_instruction(target->last_instruction());
82 target->UnuseAllInputs();
83 return replacement;
84}
85
86BranchInstr* BranchSimplifier::CloneBranch(Zone* zone,
87 BranchInstr* branch,
88 Value* new_left,
89 Value* new_right) {
90 ComparisonInstr* comparison = branch->comparison();
91 ComparisonInstr* new_comparison =
92 comparison->CopyWithNewOperands(new_left, new_right);
93 BranchInstr* new_branch =
94 new (zone) BranchInstr(new_comparison, DeoptId::kNone);
95 return new_branch;
96}
97
99 // Optimize some branches that test the value of a phi. When it is safe
100 // to do so, push the branch to each of the predecessor blocks. This is
101 // an optimization when (a) it can avoid materializing a boolean object at
102 // the phi only to test its value, and (b) it can expose opportunities for
103 // constant propagation and unreachable code elimination. This
104 // optimization is intended to run after inlining which creates
105 // opportunities for optimization (a) and before constant folding which
106 // can perform optimization (b).
107
108 // Begin with a worklist of join blocks ending in branches. They are
109 // candidates for the pattern below.
110 Zone* zone = flow_graph->zone();
111 const GrowableArray<BlockEntryInstr*>& postorder = flow_graph->postorder();
113 for (BlockIterator it(postorder); !it.Done(); it.Advance()) {
114 BlockEntryInstr* block = it.Current();
115 if (block->IsJoinEntry() && block->last_instruction()->IsBranch()) {
116 worklist.Add(block);
117 }
118 }
119
120 // Rewrite until no more instance of the pattern exists.
121 bool changed = false;
122 while (!worklist.is_empty()) {
123 // All blocks in the worklist are join blocks (ending with a branch).
124 JoinEntryInstr* block = worklist.RemoveLast()->AsJoinEntry();
125 ASSERT(block != nullptr);
126
127 if (Match(block)) {
128 changed = true;
129
130 // The branch will be copied and pushed to all the join's
131 // predecessors. Convert the true and false target blocks into join
132 // blocks to join the control flows from all of the true
133 // (respectively, false) targets of the copied branches.
134 //
135 // The converted join block will have no phis, so it cannot be another
136 // instance of the pattern. There is thus no need to add it to the
137 // worklist.
138 BranchInstr* branch = block->last_instruction()->AsBranch();
139 ASSERT(branch != nullptr);
140 JoinEntryInstr* join_true = ToJoinEntry(zone, branch->true_successor());
141 JoinEntryInstr* join_false = ToJoinEntry(zone, branch->false_successor());
142
143 ComparisonInstr* comparison = branch->comparison();
144 PhiInstr* phi = comparison->left()->definition()->AsPhi();
145 ConstantInstr* constant = comparison->right()->definition()->AsConstant();
146 ASSERT(constant != nullptr);
147 // Copy the constant and branch and push it to all the predecessors.
148 for (intptr_t i = 0, count = block->PredecessorCount(); i < count; ++i) {
149 GotoInstr* old_goto =
150 block->PredecessorAt(i)->last_instruction()->AsGoto();
151 ASSERT(old_goto != nullptr);
152
153 // Replace the goto in each predecessor with a rewritten branch,
154 // rewritten to use the corresponding phi input instead of the phi.
155 Value* new_left = phi->InputAt(i)->Copy(zone);
156 Value* new_right = new (zone) Value(constant);
157 BranchInstr* new_branch =
158 CloneBranch(zone, branch, new_left, new_right);
159 if (branch->env() == nullptr) {
160 new_branch->InheritDeoptTarget(zone, old_goto);
161 } else {
162 // Take the environment from the branch if it has one.
163 new_branch->InheritDeoptTarget(zone, branch);
164 // InheritDeoptTarget gave the new branch's comparison the same
165 // deopt id that it gave the new branch. The id should be the
166 // deopt id of the original comparison.
167 new_branch->comparison()->SetDeoptId(*comparison);
168 // The phi can be used in the branch's environment. Rename such
169 // uses.
170 Definition* replacement = phi->InputAt(i)->definition();
171 new_branch->ReplaceInEnvironment(phi, replacement);
172 }
173
174 new_branch->InsertBefore(old_goto);
175 new_branch->set_next(nullptr); // Detaching the goto from the graph.
176 old_goto->UnuseAllInputs();
177
178 // Update the predecessor block. We may have created another
179 // instance of the pattern so add it to the worklist if necessary.
180 BlockEntryInstr* branch_block = new_branch->GetBlock();
181 branch_block->set_last_instruction(new_branch);
182 if (branch_block->IsJoinEntry()) worklist.Add(branch_block);
183
184 // Connect the branch to the true and false joins, via empty target
185 // blocks.
186 TargetEntryInstr* true_target = new (zone) TargetEntryInstr(
187 flow_graph->max_block_id() + 1, block->try_index(), DeoptId::kNone);
188 true_target->InheritDeoptTarget(zone, join_true);
189 TargetEntryInstr* false_target = new (zone) TargetEntryInstr(
190 flow_graph->max_block_id() + 2, block->try_index(), DeoptId::kNone);
191 false_target->InheritDeoptTarget(zone, join_false);
192 flow_graph->set_max_block_id(flow_graph->max_block_id() + 2);
193 *new_branch->true_successor_address() = true_target;
194 *new_branch->false_successor_address() = false_target;
195 GotoInstr* goto_true = new (zone) GotoInstr(join_true, DeoptId::kNone);
196 goto_true->InheritDeoptTarget(zone, join_true);
197 true_target->LinkTo(goto_true);
198 true_target->set_last_instruction(goto_true);
199 GotoInstr* goto_false =
200 new (zone) GotoInstr(join_false, DeoptId::kNone);
201 goto_false->InheritDeoptTarget(zone, join_false);
202 false_target->LinkTo(goto_false);
203 false_target->set_last_instruction(goto_false);
204 }
205 // When all predecessors have been rewritten, the original block is
206 // unreachable from the graph.
207 phi->UnuseAllInputs();
208 branch->UnuseAllInputs();
209 block->UnuseAllInputs();
210 ASSERT(!phi->HasUses());
211 }
212 }
213
214 if (changed) {
215 // We may have changed the block order and the dominator tree.
216 flow_graph->DiscoverBlocks();
217 GrowableArray<BitVector*> dominance_frontier;
218 flow_graph->ComputeDominators(&dominance_frontier);
219 }
220}
221
222static bool IsTrivialBlock(BlockEntryInstr* block, Definition* defn) {
223 return (block->IsTargetEntry() && (block->PredecessorCount() == 1)) &&
224 ((block->next() == block->last_instruction()) ||
225 ((block->next() == defn) &&
226 (defn->next() == block->last_instruction())));
227}
228
230 Definition* instr,
231 IfThenElseInstr* before) {
232 block->UnuseAllInputs();
234
235 if ((block->next() == instr) &&
236 (instr->next() == block->last_instruction())) {
237 before->previous()->LinkTo(instr);
238 instr->LinkTo(before);
239 }
240}
241
243 Zone* zone = flow_graph->zone();
244 bool changed = false;
245
246 const GrowableArray<BlockEntryInstr*>& postorder = flow_graph->postorder();
247 for (BlockIterator it(postorder); !it.Done(); it.Advance()) {
248 BlockEntryInstr* block = it.Current();
249 JoinEntryInstr* join = block->AsJoinEntry();
250
251 // Detect diamond control flow pattern which materializes a value depending
252 // on the result of the comparison:
253 //
254 // B_pred:
255 // ...
256 // Branch if COMP goto (B_pred1, B_pred2)
257 // B_pred1: -- trivial block that contains at most one definition
258 // v1 = Constant(...)
259 // goto B_block
260 // B_pred2: -- trivial block that contains at most one definition
261 // v2 = Constant(...)
262 // goto B_block
263 // B_block:
264 // v3 = phi(v1, v2) -- single phi
265 //
266 // and replace it with
267 //
268 // Ba:
269 // v3 = IfThenElse(COMP ? v1 : v2)
270 //
271 if ((join != nullptr) && (join->phis() != nullptr) &&
272 (join->phis()->length() == 1) && (block->PredecessorCount() == 2)) {
273 BlockEntryInstr* pred1 = block->PredecessorAt(0);
274 BlockEntryInstr* pred2 = block->PredecessorAt(1);
275
276 PhiInstr* phi = (*join->phis())[0];
277 Value* v1 = phi->InputAt(0);
278 Value* v2 = phi->InputAt(1);
279
280 if (IsTrivialBlock(pred1, v1->definition()) &&
281 IsTrivialBlock(pred2, v2->definition()) &&
282 (pred1->PredecessorAt(0) == pred2->PredecessorAt(0))) {
283 BlockEntryInstr* pred = pred1->PredecessorAt(0);
284 BranchInstr* branch = pred->last_instruction()->AsBranch();
285
286 if (branch == nullptr) {
287 // There is no "B_pred" block, or the block is the IndirectGoto
288 // of a switch that uses it as a jump table.
289 ASSERT(pred->last_instruction()->IsGraphEntry() ||
290 pred->last_instruction()->IsIndirectGoto());
291 continue;
292 }
293
294 ComparisonInstr* comparison = branch->comparison();
295
296 // Check if the platform supports efficient branchless IfThenElseInstr
297 // for the given combination of comparison and values flowing from
298 // false and true paths.
299 if (IfThenElseInstr::Supports(comparison, v1, v2)) {
300 Value* if_true = (pred1 == branch->true_successor()) ? v1 : v2;
301 Value* if_false = (pred2 == branch->true_successor()) ? v1 : v2;
302
303 ComparisonInstr* new_comparison = comparison->CopyWithNewOperands(
304 comparison->left()->Copy(zone), comparison->right()->Copy(zone));
306 new (zone) IfThenElseInstr(new_comparison, if_true->Copy(zone),
307 if_false->Copy(zone), DeoptId::kNone);
308 flow_graph->InsertBefore(branch, if_then_else, nullptr,
310
312
313 // Connect IfThenElseInstr to the first instruction in the merge block
314 // effectively eliminating diamond control flow.
315 // Current block as well as pred1 and pred2 blocks are no longer in
316 // the graph at this point.
317 if_then_else->LinkTo(join->next());
318 pred->set_last_instruction(join->last_instruction());
319
320 // Resulting block must inherit block id from the eliminated current
321 // block to guarantee that ordering of phi operands in its successor
322 // stays consistent.
323 pred->set_block_id(block->block_id());
324
325 // If v1 and v2 were defined inside eliminated blocks pred1/pred2
326 // move them out to the place before inserted IfThenElse instruction.
328 EliminateTrivialBlock(pred2, v2->definition(), if_then_else);
329
330 // Update use lists to reflect changes in the graph.
331 phi->UnuseAllInputs();
332 branch->UnuseAllInputs();
333 block->UnuseAllInputs();
334
335 // The graph has changed. Recompute dominators and block orders after
336 // this pass is finished.
337 changed = true;
338 }
339 }
340 }
341 }
342
343 if (changed) {
344 // We may have changed the block order and the dominator tree.
345 flow_graph->DiscoverBlocks();
346 GrowableArray<BitVector*> dominance_frontier;
347 flow_graph->ComputeDominators(&dominance_frontier);
348 }
349}
350
351} // namespace dart
int count
Definition: FontMgrTest.cpp:50
SI T if_then_else(C cond, T t, T e)
Vec2Value v2
intptr_t length() const
intptr_t try_index() const
Definition: il.h:1730
void set_block_id(intptr_t block_id)
Definition: il.h:1753
intptr_t block_id() const
Definition: il.h:1661
virtual intptr_t PredecessorCount() const =0
virtual BlockEntryInstr * PredecessorAt(intptr_t index) const =0
void set_last_instruction(Instruction *instr)
Definition: il.h:1687
Instruction * last_instruction() const
Definition: il.h:1686
bool Done() const
Definition: flow_graph.h:46
TargetEntryInstr ** false_successor_address()
Definition: il.h:4051
TargetEntryInstr * false_successor() const
Definition: il.h:4048
TargetEntryInstr ** true_successor_address()
Definition: il.h:4050
TargetEntryInstr * true_successor() const
Definition: il.h:4047
ComparisonInstr * comparison() const
Definition: il.h:4021
static TargetEntryInstr * ToTargetEntry(Zone *zone, BlockEntryInstr *target)
static JoinEntryInstr * ToJoinEntry(Zone *zone, BlockEntryInstr *target)
static void Simplify(FlowGraph *flow_graph)
void SetDeoptId(const Instruction &instr)
Definition: il.h:3874
Value * right() const
Definition: il.h:3847
Value * left() const
Definition: il.h:3846
virtual ComparisonInstr * CopyWithNewOperands(Value *left, Value *right)=0
bool HasUses() const
Definition: il.h:2569
Value * env_use_list() const
Definition: il.h:2578
Value * input_use_list() const
Definition: il.h:2575
void ReplaceUsesWith(Definition *other)
Definition: il.cc:1493
static constexpr intptr_t kNone
Definition: deopt_id.h:27
Zone * zone() const
Definition: flow_graph.h:261
intptr_t max_block_id() const
Definition: flow_graph.h:264
void DiscoverBlocks()
Definition: flow_graph.cc:346
const GrowableArray< BlockEntryInstr * > & postorder() const
Definition: flow_graph.h:204
void ComputeDominators(GrowableArray< BitVector * > *dominance_frontier)
Definition: flow_graph.cc:975
void set_max_block_id(intptr_t id)
Definition: flow_graph.h:265
void InsertBefore(Instruction *next, Instruction *instr, Environment *env, UseKind use_kind)
Definition: flow_graph.h:312
static void Simplify(FlowGraph *flow_graph)
static bool Supports(ComparisonInstr *comparison, Value *v1, Value *v2)
Definition: il.cc:6613
void InsertBefore(Instruction *next)
Definition: il.h:1280
Instruction * next() const
Definition: il.h:1093
void LinkTo(Instruction *next)
Definition: il.h:1108
void InheritDeoptTarget(Zone *zone, Instruction *other)
Definition: il.cc:1569
virtual BlockEntryInstr * GetBlock()
Definition: il.cc:1352
Environment * env() const
Definition: il.h:1215
void UnuseAllInputs()
Definition: il.cc:1534
void set_next(Instruction *instr)
Definition: il.h:1094
void ReplaceInEnvironment(Definition *current, Definition *replacement)
Definition: il.cc:1289
Instruction * previous() const
Definition: il.h:1087
virtual BlockEntryInstr * PredecessorAt(intptr_t index) const
Definition: il.h:2066
virtual intptr_t PredecessorCount() const
Definition: il.h:2065
JoinEntryInstr * block() const
Definition: il.h:2817
Definition: il.h:75
Value * Copy(Zone *zone)
Definition: il.h:134
Instruction * instruction() const
Definition: il.h:121
Value * next_use() const
Definition: il.h:114
Definition * definition() const
Definition: il.h:103
Value * InputAt(intptr_t i) const
Definition: il.h:2795
#define ASSERT(E)
uint32_t * target
Definition: dart_vm.cc:33
static bool IsTrivialBlock(BlockEntryInstr *block, Definition *defn)
static bool PhiHasSingleUse(PhiInstr *phi, Value *use)
static void EliminateTrivialBlock(BlockEntryInstr *block, Definition *instr, IfThenElseInstr *before)
static SkString join(const CommandLineFlags::StringArray &)
Definition: skpbench.cpp:741