1 | // Copyright (c) 2013, 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 | |
5 | #include "vm/compiler/backend/constant_propagator.h" |
6 | |
7 | #include "vm/bit_vector.h" |
8 | #include "vm/compiler/backend/evaluator.h" |
9 | #include "vm/compiler/backend/flow_graph_compiler.h" |
10 | #include "vm/compiler/backend/il.h" |
11 | #include "vm/compiler/backend/il_printer.h" |
12 | #include "vm/compiler/backend/range_analysis.h" |
13 | #include "vm/compiler/frontend/flow_graph_builder.h" |
14 | #include "vm/parser.h" |
15 | #include "vm/symbols.h" |
16 | |
17 | namespace dart { |
18 | |
19 | DEFINE_FLAG(bool, remove_redundant_phis, true, "Remove redundant phis." ); |
20 | DEFINE_FLAG(bool, |
21 | trace_constant_propagation, |
22 | false, |
23 | "Print constant propagation and useless code elimination." ); |
24 | |
25 | // Quick access to the current zone and isolate. |
26 | #define I (isolate()) |
27 | #define Z (graph_->zone()) |
28 | #define T (graph_->thread()) |
29 | |
30 | ConstantPropagator::ConstantPropagator( |
31 | FlowGraph* graph, |
32 | const GrowableArray<BlockEntryInstr*>& ignored) |
33 | : FlowGraphVisitor(ignored), |
34 | graph_(graph), |
35 | unknown_(Object::unknown_constant()), |
36 | non_constant_(Object::non_constant()), |
37 | constant_value_(Object::Handle(Z)), |
38 | reachable_(new (Z) BitVector(Z, graph->preorder().length())), |
39 | unwrapped_phis_(new (Z) |
40 | BitVector(Z, graph->max_virtual_register_number())), |
41 | block_worklist_(), |
42 | definition_worklist_(graph, 10) {} |
43 | |
44 | void ConstantPropagator::Optimize(FlowGraph* graph) { |
45 | GrowableArray<BlockEntryInstr*> ignored; |
46 | ConstantPropagator cp(graph, ignored); |
47 | cp.Analyze(); |
48 | cp.Transform(); |
49 | } |
50 | |
51 | void ConstantPropagator::OptimizeBranches(FlowGraph* graph) { |
52 | GrowableArray<BlockEntryInstr*> ignored; |
53 | ConstantPropagator cp(graph, ignored); |
54 | cp.Analyze(); |
55 | cp.Transform(); |
56 | cp.EliminateRedundantBranches(); |
57 | } |
58 | |
59 | void ConstantPropagator::SetReachable(BlockEntryInstr* block) { |
60 | if (!reachable_->Contains(block->preorder_number())) { |
61 | reachable_->Add(block->preorder_number()); |
62 | block_worklist_.Add(block); |
63 | } |
64 | } |
65 | |
66 | bool ConstantPropagator::SetValue(Definition* definition, const Object& value) { |
67 | // We would like to assert we only go up (toward non-constant) in the lattice. |
68 | // |
69 | // ASSERT(IsUnknown(definition->constant_value()) || |
70 | // IsNonConstant(value) || |
71 | // (definition->constant_value().raw() == value.raw())); |
72 | // |
73 | // But the final disjunct is not true (e.g., mint or double constants are |
74 | // heap-allocated and so not necessarily pointer-equal on each iteration). |
75 | if (definition->constant_value().raw() != value.raw()) { |
76 | definition->constant_value() = value.raw(); |
77 | if (definition->input_use_list() != NULL) { |
78 | definition_worklist_.Add(definition); |
79 | } |
80 | return true; |
81 | } |
82 | return false; |
83 | } |
84 | |
85 | static bool IsIdenticalConstants(const Object& left, const Object& right) { |
86 | // This should be kept in line with Identical_comparison (identical.cc) |
87 | // (=> Instance::IsIdenticalTo in object.cc). |
88 | |
89 | if (left.raw() == right.raw()) return true; |
90 | if (left.GetClassId() != right.GetClassId()) return false; |
91 | if (left.IsInteger()) { |
92 | return Integer::Cast(left).Equals(Integer::Cast(right)); |
93 | } |
94 | if (left.IsDouble()) { |
95 | return Double::Cast(left).BitwiseEqualsToDouble( |
96 | Double::Cast(right).value()); |
97 | } |
98 | return false; |
99 | } |
100 | |
101 | // Compute the join of two values in the lattice, assign it to the first. |
102 | void ConstantPropagator::Join(Object* left, const Object& right) { |
103 | // Join(non-constant, X) = non-constant |
104 | // Join(X, unknown) = X |
105 | if (IsNonConstant(*left) || IsUnknown(right)) return; |
106 | |
107 | // Join(unknown, X) = X |
108 | // Join(X, non-constant) = non-constant |
109 | if (IsUnknown(*left) || IsNonConstant(right)) { |
110 | *left = right.raw(); |
111 | return; |
112 | } |
113 | |
114 | // Join(X, X) = X |
115 | if (IsIdenticalConstants(*left, right)) return; |
116 | |
117 | // Join(X, Y) = non-constant |
118 | *left = non_constant_.raw(); |
119 | } |
120 | |
121 | // -------------------------------------------------------------------------- |
122 | // Analysis of blocks. Called at most once per block. The block is already |
123 | // marked as reachable. All instructions in the block are analyzed. |
124 | void ConstantPropagator::VisitGraphEntry(GraphEntryInstr* block) { |
125 | for (auto def : *block->initial_definitions()) { |
126 | def->Accept(this); |
127 | } |
128 | ASSERT(ForwardInstructionIterator(block).Done()); |
129 | |
130 | // TODO(fschneider): Improve this approximation. The catch entry is only |
131 | // reachable if a call in the try-block is reachable. |
132 | for (intptr_t i = 0; i < block->SuccessorCount(); ++i) { |
133 | SetReachable(block->SuccessorAt(i)); |
134 | } |
135 | } |
136 | |
137 | void ConstantPropagator::VisitFunctionEntry(FunctionEntryInstr* block) { |
138 | for (auto def : *block->initial_definitions()) { |
139 | def->Accept(this); |
140 | } |
141 | for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) { |
142 | it.Current()->Accept(this); |
143 | } |
144 | } |
145 | |
146 | void ConstantPropagator::VisitNativeEntry(NativeEntryInstr* block) { |
147 | VisitFunctionEntry(block); |
148 | } |
149 | |
150 | void ConstantPropagator::VisitOsrEntry(OsrEntryInstr* block) { |
151 | for (auto def : *block->initial_definitions()) { |
152 | def->Accept(this); |
153 | } |
154 | for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) { |
155 | it.Current()->Accept(this); |
156 | } |
157 | } |
158 | |
159 | void ConstantPropagator::VisitCatchBlockEntry(CatchBlockEntryInstr* block) { |
160 | for (auto def : *block->initial_definitions()) { |
161 | def->Accept(this); |
162 | } |
163 | for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) { |
164 | it.Current()->Accept(this); |
165 | } |
166 | } |
167 | |
168 | void ConstantPropagator::VisitJoinEntry(JoinEntryInstr* block) { |
169 | // Phis are visited when visiting Goto at a predecessor. See VisitGoto. |
170 | for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) { |
171 | it.Current()->Accept(this); |
172 | } |
173 | } |
174 | |
175 | void ConstantPropagator::VisitTargetEntry(TargetEntryInstr* block) { |
176 | for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) { |
177 | it.Current()->Accept(this); |
178 | } |
179 | } |
180 | |
181 | void ConstantPropagator::VisitIndirectEntry(IndirectEntryInstr* block) { |
182 | for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) { |
183 | it.Current()->Accept(this); |
184 | } |
185 | } |
186 | |
187 | void ConstantPropagator::VisitParallelMove(ParallelMoveInstr* instr) { |
188 | // Parallel moves have not yet been inserted in the graph. |
189 | UNREACHABLE(); |
190 | } |
191 | |
192 | // -------------------------------------------------------------------------- |
193 | // Analysis of control instructions. Unconditional successors are |
194 | // reachable. Conditional successors are reachable depending on the |
195 | // constant value of the condition. |
196 | void ConstantPropagator::VisitReturn(ReturnInstr* instr) { |
197 | // Nothing to do. |
198 | } |
199 | |
200 | void ConstantPropagator::VisitNativeReturn(NativeReturnInstr* instr) { |
201 | // Nothing to do. |
202 | } |
203 | |
204 | void ConstantPropagator::VisitThrow(ThrowInstr* instr) { |
205 | // Nothing to do. |
206 | } |
207 | |
208 | void ConstantPropagator::VisitReThrow(ReThrowInstr* instr) { |
209 | // Nothing to do. |
210 | } |
211 | |
212 | void ConstantPropagator::VisitStop(StopInstr* instr) { |
213 | // Nothing to do. |
214 | } |
215 | |
216 | void ConstantPropagator::VisitGoto(GotoInstr* instr) { |
217 | SetReachable(instr->successor()); |
218 | |
219 | // Phi value depends on the reachability of a predecessor. We have |
220 | // to revisit phis every time a predecessor becomes reachable. |
221 | for (PhiIterator it(instr->successor()); !it.Done(); it.Advance()) { |
222 | PhiInstr* phi = it.Current(); |
223 | phi->Accept(this); |
224 | |
225 | // If this phi was previously unwrapped as redundant and it is no longer |
226 | // redundant (does not unwrap) then we need to revisit the uses. |
227 | if (unwrapped_phis_->Contains(phi->ssa_temp_index()) && |
228 | (UnwrapPhi(phi) == phi)) { |
229 | unwrapped_phis_->Remove(phi->ssa_temp_index()); |
230 | definition_worklist_.Add(phi); |
231 | } |
232 | } |
233 | } |
234 | |
235 | void ConstantPropagator::VisitIndirectGoto(IndirectGotoInstr* instr) { |
236 | for (intptr_t i = 0; i < instr->SuccessorCount(); i++) { |
237 | SetReachable(instr->SuccessorAt(i)); |
238 | } |
239 | } |
240 | |
241 | void ConstantPropagator::VisitBranch(BranchInstr* instr) { |
242 | instr->comparison()->Accept(this); |
243 | |
244 | // The successors may be reachable, but only if this instruction is. (We |
245 | // might be analyzing it because the constant value of one of its inputs |
246 | // has changed.) |
247 | if (reachable_->Contains(instr->GetBlock()->preorder_number())) { |
248 | if (instr->constant_target() != NULL) { |
249 | ASSERT((instr->constant_target() == instr->true_successor()) || |
250 | (instr->constant_target() == instr->false_successor())); |
251 | SetReachable(instr->constant_target()); |
252 | } else { |
253 | const Object& value = instr->comparison()->constant_value(); |
254 | if (IsNonConstant(value)) { |
255 | SetReachable(instr->true_successor()); |
256 | SetReachable(instr->false_successor()); |
257 | } else if (value.raw() == Bool::True().raw()) { |
258 | SetReachable(instr->true_successor()); |
259 | } else if (!IsUnknown(value)) { // Any other constant. |
260 | SetReachable(instr->false_successor()); |
261 | } |
262 | } |
263 | } |
264 | } |
265 | |
266 | // -------------------------------------------------------------------------- |
267 | // Analysis of non-definition instructions. They do not have values so they |
268 | // cannot have constant values. |
269 | void ConstantPropagator::VisitCheckStackOverflow( |
270 | CheckStackOverflowInstr* instr) {} |
271 | |
272 | void ConstantPropagator::VisitCheckClass(CheckClassInstr* instr) {} |
273 | |
274 | void ConstantPropagator::VisitCheckCondition(CheckConditionInstr* instr) {} |
275 | |
276 | void ConstantPropagator::VisitCheckClassId(CheckClassIdInstr* instr) {} |
277 | |
278 | void ConstantPropagator::VisitGuardFieldClass(GuardFieldClassInstr* instr) {} |
279 | |
280 | void ConstantPropagator::VisitGuardFieldLength(GuardFieldLengthInstr* instr) {} |
281 | |
282 | void ConstantPropagator::VisitGuardFieldType(GuardFieldTypeInstr* instr) {} |
283 | |
284 | void ConstantPropagator::VisitCheckSmi(CheckSmiInstr* instr) {} |
285 | |
286 | void ConstantPropagator::VisitTailCall(TailCallInstr* instr) {} |
287 | |
288 | void ConstantPropagator::VisitCheckEitherNonSmi(CheckEitherNonSmiInstr* instr) { |
289 | } |
290 | |
291 | void ConstantPropagator::VisitStoreUntagged(StoreUntaggedInstr* instr) {} |
292 | |
293 | void ConstantPropagator::VisitStoreIndexedUnsafe( |
294 | StoreIndexedUnsafeInstr* instr) {} |
295 | |
296 | void ConstantPropagator::VisitStoreIndexed(StoreIndexedInstr* instr) {} |
297 | |
298 | void ConstantPropagator::VisitStoreInstanceField( |
299 | StoreInstanceFieldInstr* instr) {} |
300 | |
301 | void ConstantPropagator::VisitMemoryCopy(MemoryCopyInstr* instr) {} |
302 | |
303 | void ConstantPropagator::VisitDeoptimize(DeoptimizeInstr* instr) { |
304 | // TODO(vegorov) remove all code after DeoptimizeInstr as dead. |
305 | } |
306 | |
307 | Definition* ConstantPropagator::UnwrapPhi(Definition* defn) { |
308 | if (defn->IsPhi()) { |
309 | JoinEntryInstr* block = defn->AsPhi()->block(); |
310 | |
311 | Definition* input = NULL; |
312 | for (intptr_t i = 0; i < defn->InputCount(); ++i) { |
313 | if (reachable_->Contains(block->PredecessorAt(i)->preorder_number())) { |
314 | if (input == NULL) { |
315 | input = defn->InputAt(i)->definition(); |
316 | } else { |
317 | return defn; |
318 | } |
319 | } |
320 | } |
321 | |
322 | return input; |
323 | } |
324 | |
325 | return defn; |
326 | } |
327 | |
328 | void ConstantPropagator::MarkUnwrappedPhi(Definition* phi) { |
329 | ASSERT(phi->IsPhi()); |
330 | unwrapped_phis_->Add(phi->ssa_temp_index()); |
331 | } |
332 | |
333 | ConstantPropagator::PhiInfo* ConstantPropagator::GetPhiInfo(PhiInstr* phi) { |
334 | if (phi->HasPassSpecificId(CompilerPass::kConstantPropagation)) { |
335 | const intptr_t id = |
336 | phi->GetPassSpecificId(CompilerPass::kConstantPropagation); |
337 | // Note: id might have been assigned by the previous round of constant |
338 | // propagation, so we need to verify it before using it. |
339 | if (id < phis_.length() && phis_[id].phi == phi) { |
340 | return &phis_[id]; |
341 | } |
342 | } |
343 | |
344 | phi->SetPassSpecificId(CompilerPass::kConstantPropagation, phis_.length()); |
345 | phis_.Add({phi, 0}); |
346 | return &phis_.Last(); |
347 | } |
348 | |
349 | // -------------------------------------------------------------------------- |
350 | // Analysis of definitions. Compute the constant value. If it has changed |
351 | // and the definition has input uses, add the definition to the definition |
352 | // worklist so that the used can be processed. |
353 | void ConstantPropagator::VisitPhi(PhiInstr* instr) { |
354 | // Detect convergence issues by checking if visit count for this phi |
355 | // is too high. We should only visit this phi once for every predecessor |
356 | // becoming reachable, once for every input changing its constant value and |
357 | // once for an unwrapped redundant phi becoming non-redundant. |
358 | // Inputs can only change their constant value at most three times: from |
359 | // non-constant to unknown to specific constant to non-constant. The first |
360 | // link (non-constant to ...) can happen when we run the second round of |
361 | // constant propagation - some instructions can have non-constant assigned to |
362 | // them at the end of the previous constant propagation. |
363 | auto info = GetPhiInfo(instr); |
364 | info->visit_count++; |
365 | const intptr_t kMaxVisitsExpected = 5 * instr->InputCount(); |
366 | if (info->visit_count > kMaxVisitsExpected) { |
367 | OS::PrintErr( |
368 | "ConstantPropagation pass is failing to converge on graph for %s\n" , |
369 | graph_->parsed_function().function().ToCString()); |
370 | OS::PrintErr("Phi %s was visited %" Pd " times\n" , instr->ToCString(), |
371 | info->visit_count); |
372 | NOT_IN_PRODUCT( |
373 | FlowGraphPrinter::PrintGraph("Constant Propagation" , graph_)); |
374 | FATAL("Aborting due to non-covergence." ); |
375 | } |
376 | |
377 | // Compute the join over all the reachable predecessor values. |
378 | JoinEntryInstr* block = instr->block(); |
379 | Object& value = Object::ZoneHandle(Z, Unknown()); |
380 | for (intptr_t pred_idx = 0; pred_idx < instr->InputCount(); ++pred_idx) { |
381 | if (reachable_->Contains( |
382 | block->PredecessorAt(pred_idx)->preorder_number())) { |
383 | Join(&value, instr->InputAt(pred_idx)->definition()->constant_value()); |
384 | } |
385 | } |
386 | SetValue(instr, value); |
387 | } |
388 | |
389 | void ConstantPropagator::VisitRedefinition(RedefinitionInstr* instr) { |
390 | const Object& value = instr->value()->definition()->constant_value(); |
391 | if (IsConstant(value)) { |
392 | SetValue(instr, value); |
393 | } else { |
394 | SetValue(instr, non_constant_); |
395 | } |
396 | } |
397 | |
398 | void ConstantPropagator::VisitReachabilityFence(ReachabilityFenceInstr* instr) { |
399 | // Nothing to do. |
400 | } |
401 | |
402 | void ConstantPropagator::VisitCheckArrayBound(CheckArrayBoundInstr* instr) { |
403 | // Don't propagate constants through check, since it would eliminate |
404 | // the data dependence between the bound check and the load/store. |
405 | // Graph finalization will expose the constant eventually. |
406 | SetValue(instr, non_constant_); |
407 | } |
408 | |
409 | void ConstantPropagator::VisitGenericCheckBound(GenericCheckBoundInstr* instr) { |
410 | // Don't propagate constants through check, since it would eliminate |
411 | // the data dependence between the bound check and the load/store. |
412 | // Graph finalization will expose the constant eventually. |
413 | SetValue(instr, non_constant_); |
414 | } |
415 | |
416 | void ConstantPropagator::VisitCheckNull(CheckNullInstr* instr) { |
417 | // Don't propagate constants through check, since it would eliminate |
418 | // the data dependence between the null check and its use. |
419 | // Graph finalization will expose the constant eventually. |
420 | SetValue(instr, non_constant_); |
421 | } |
422 | |
423 | void ConstantPropagator::VisitParameter(ParameterInstr* instr) { |
424 | SetValue(instr, non_constant_); |
425 | } |
426 | |
427 | void ConstantPropagator::VisitNativeParameter(NativeParameterInstr* instr) { |
428 | SetValue(instr, non_constant_); |
429 | } |
430 | |
431 | void ConstantPropagator::VisitPushArgument(PushArgumentInstr* instr) { |
432 | UNREACHABLE(); |
433 | } |
434 | |
435 | void ConstantPropagator::VisitAssertAssignable(AssertAssignableInstr* instr) { |
436 | const auto& value = instr->value()->definition()->constant_value(); |
437 | const auto& dst_type = instr->dst_type()->definition()->constant_value(); |
438 | |
439 | if (IsNonConstant(value) || IsNonConstant(dst_type)) { |
440 | SetValue(instr, non_constant_); |
441 | } else if (IsConstant(value) && IsConstant(dst_type)) { |
442 | // We are ignoring the instantiator and instantiator_type_arguments, but |
443 | // still monotonic and safe. |
444 | if (instr->value()->Type()->IsAssignableTo(AbstractType::Cast(dst_type))) { |
445 | SetValue(instr, value); |
446 | } else { |
447 | SetValue(instr, non_constant_); |
448 | } |
449 | } |
450 | } |
451 | |
452 | void ConstantPropagator::VisitAssertSubtype(AssertSubtypeInstr* instr) {} |
453 | |
454 | void ConstantPropagator::VisitAssertBoolean(AssertBooleanInstr* instr) { |
455 | const Object& value = instr->value()->definition()->constant_value(); |
456 | if (IsNonConstant(value)) { |
457 | SetValue(instr, non_constant_); |
458 | } else if (IsConstant(value)) { |
459 | if (value.IsBool()) { |
460 | SetValue(instr, value); |
461 | } else { |
462 | SetValue(instr, non_constant_); |
463 | } |
464 | } |
465 | } |
466 | |
467 | void ConstantPropagator::VisitSpecialParameter(SpecialParameterInstr* instr) { |
468 | SetValue(instr, non_constant_); |
469 | } |
470 | |
471 | void ConstantPropagator::VisitClosureCall(ClosureCallInstr* instr) { |
472 | SetValue(instr, non_constant_); |
473 | } |
474 | |
475 | void ConstantPropagator::VisitInstanceCall(InstanceCallInstr* instr) { |
476 | SetValue(instr, non_constant_); |
477 | } |
478 | |
479 | void ConstantPropagator::VisitPolymorphicInstanceCall( |
480 | PolymorphicInstanceCallInstr* instr) { |
481 | SetValue(instr, non_constant_); |
482 | } |
483 | |
484 | void ConstantPropagator::VisitDispatchTableCall(DispatchTableCallInstr* instr) { |
485 | SetValue(instr, non_constant_); |
486 | } |
487 | |
488 | void ConstantPropagator::VisitStaticCall(StaticCallInstr* instr) { |
489 | const auto kind = instr->function().recognized_kind(); |
490 | switch (kind) { |
491 | case MethodRecognizer::kOneByteString_equality: |
492 | case MethodRecognizer::kTwoByteString_equality: { |
493 | ASSERT(instr->FirstArgIndex() == 0); |
494 | // Use pure identity as a fast equality test. |
495 | if (instr->ArgumentAt(0)->OriginalDefinition() == |
496 | instr->ArgumentAt(1)->OriginalDefinition()) { |
497 | SetValue(instr, Bool::True()); |
498 | return; |
499 | } |
500 | // Otherwise evaluate string compare with propagated constants. |
501 | const Object& o1 = instr->ArgumentAt(0)->constant_value(); |
502 | const Object& o2 = instr->ArgumentAt(1)->constant_value(); |
503 | if (IsConstant(o1) && IsConstant(o2) && o1.IsString() && o2.IsString()) { |
504 | SetValue(instr, Bool::Get(String::Cast(o1).Equals(String::Cast(o2)))); |
505 | return; |
506 | } |
507 | break; |
508 | } |
509 | case MethodRecognizer::kStringBaseLength: |
510 | case MethodRecognizer::kStringBaseIsEmpty: { |
511 | ASSERT(instr->FirstArgIndex() == 0); |
512 | // Otherwise evaluate string length with propagated constants. |
513 | const Object& o = instr->ArgumentAt(0)->constant_value(); |
514 | if (IsConstant(o) && o.IsString()) { |
515 | const auto& str = String::Cast(o); |
516 | if (kind == MethodRecognizer::kStringBaseLength) { |
517 | SetValue(instr, Integer::ZoneHandle(Z, Integer::New(str.Length()))); |
518 | } else { |
519 | SetValue(instr, Bool::Get(str.Length() == 0)); |
520 | } |
521 | return; |
522 | } |
523 | break; |
524 | } |
525 | default: |
526 | break; |
527 | } |
528 | SetValue(instr, non_constant_); |
529 | } |
530 | |
531 | void ConstantPropagator::VisitLoadLocal(LoadLocalInstr* instr) { |
532 | // Instruction is eliminated when translating to SSA. |
533 | UNREACHABLE(); |
534 | } |
535 | |
536 | void ConstantPropagator::VisitDropTemps(DropTempsInstr* instr) { |
537 | // Instruction is eliminated when translating to SSA. |
538 | UNREACHABLE(); |
539 | } |
540 | |
541 | void ConstantPropagator::VisitMakeTemp(MakeTempInstr* instr) { |
542 | // Instruction is eliminated when translating to SSA. |
543 | UNREACHABLE(); |
544 | } |
545 | |
546 | void ConstantPropagator::VisitStoreLocal(StoreLocalInstr* instr) { |
547 | // Instruction is eliminated when translating to SSA. |
548 | UNREACHABLE(); |
549 | } |
550 | |
551 | void ConstantPropagator::VisitIfThenElse(IfThenElseInstr* instr) { |
552 | instr->comparison()->Accept(this); |
553 | const Object& value = instr->comparison()->constant_value(); |
554 | if (IsNonConstant(value)) { |
555 | SetValue(instr, non_constant_); |
556 | } else if (IsConstant(value)) { |
557 | ASSERT(!value.IsNull()); |
558 | ASSERT(value.IsBool()); |
559 | bool result = Bool::Cast(value).value(); |
560 | SetValue(instr, Smi::Handle(Z, Smi::New(result ? instr->if_true() |
561 | : instr->if_false()))); |
562 | } |
563 | } |
564 | |
565 | void ConstantPropagator::VisitStrictCompare(StrictCompareInstr* instr) { |
566 | Definition* left_defn = instr->left()->definition(); |
567 | Definition* right_defn = instr->right()->definition(); |
568 | |
569 | Definition* unwrapped_left_defn = UnwrapPhi(left_defn); |
570 | Definition* unwrapped_right_defn = UnwrapPhi(right_defn); |
571 | if (unwrapped_left_defn == unwrapped_right_defn) { |
572 | // Fold x === x, and x !== x to true/false. |
573 | if (SetValue(instr, Bool::Get(instr->kind() == Token::kEQ_STRICT))) { |
574 | if (unwrapped_left_defn != left_defn) { |
575 | MarkUnwrappedPhi(left_defn); |
576 | } |
577 | if (unwrapped_right_defn != right_defn) { |
578 | MarkUnwrappedPhi(right_defn); |
579 | } |
580 | } |
581 | return; |
582 | } |
583 | |
584 | const Object& left = left_defn->constant_value(); |
585 | const Object& right = right_defn->constant_value(); |
586 | if (IsNonConstant(left) || IsNonConstant(right)) { |
587 | // TODO(vegorov): incorporate nullability information into the lattice. |
588 | if ((left.IsNull() && instr->right()->Type()->HasDecidableNullability()) || |
589 | (right.IsNull() && instr->left()->Type()->HasDecidableNullability())) { |
590 | bool result = left.IsNull() ? instr->right()->Type()->IsNull() |
591 | : instr->left()->Type()->IsNull(); |
592 | if (instr->kind() == Token::kNE_STRICT) { |
593 | result = !result; |
594 | } |
595 | SetValue(instr, Bool::Get(result)); |
596 | } else { |
597 | const intptr_t left_cid = instr->left()->Type()->ToCid(); |
598 | const intptr_t right_cid = instr->right()->Type()->ToCid(); |
599 | // If exact classes (cids) are known and they differ, the result |
600 | // of strict compare can be computed. |
601 | // The only exception is comparison with special sentinel value |
602 | // (used for lazy initialization) which can be compared to a |
603 | // value of any type. Sentinel value has kNeverCid. |
604 | if ((left_cid != kDynamicCid) && (right_cid != kDynamicCid) && |
605 | (left_cid != kNeverCid) && (right_cid != kNeverCid) && |
606 | (left_cid != right_cid)) { |
607 | const bool result = (instr->kind() != Token::kEQ_STRICT); |
608 | SetValue(instr, Bool::Get(result)); |
609 | } else { |
610 | SetValue(instr, non_constant_); |
611 | } |
612 | } |
613 | } else if (IsConstant(left) && IsConstant(right)) { |
614 | bool result = IsIdenticalConstants(left, right); |
615 | if (instr->kind() == Token::kNE_STRICT) { |
616 | result = !result; |
617 | } |
618 | SetValue(instr, Bool::Get(result)); |
619 | } |
620 | } |
621 | |
622 | static bool CompareIntegers(Token::Kind kind, |
623 | const Integer& left, |
624 | const Integer& right) { |
625 | const int result = left.CompareWith(right); |
626 | switch (kind) { |
627 | case Token::kEQ: |
628 | return (result == 0); |
629 | case Token::kNE: |
630 | return (result != 0); |
631 | case Token::kLT: |
632 | return (result < 0); |
633 | case Token::kGT: |
634 | return (result > 0); |
635 | case Token::kLTE: |
636 | return (result <= 0); |
637 | case Token::kGTE: |
638 | return (result >= 0); |
639 | default: |
640 | UNREACHABLE(); |
641 | return false; |
642 | } |
643 | } |
644 | |
645 | // Comparison instruction that is equivalent to the (left & right) == 0 |
646 | // comparison pattern. |
647 | void ConstantPropagator::VisitTestSmi(TestSmiInstr* instr) { |
648 | const Object& left = instr->left()->definition()->constant_value(); |
649 | const Object& right = instr->right()->definition()->constant_value(); |
650 | if (IsNonConstant(left) || IsNonConstant(right)) { |
651 | SetValue(instr, non_constant_); |
652 | } else if (IsConstant(left) && IsConstant(right)) { |
653 | if (left.IsInteger() && right.IsInteger()) { |
654 | const bool result = CompareIntegers( |
655 | instr->kind(), |
656 | Integer::Handle(Z, Integer::Cast(left).BitOp(Token::kBIT_AND, |
657 | Integer::Cast(right))), |
658 | Object::smi_zero()); |
659 | SetValue(instr, result ? Bool::True() : Bool::False()); |
660 | } else { |
661 | SetValue(instr, non_constant_); |
662 | } |
663 | } |
664 | } |
665 | |
666 | void ConstantPropagator::VisitTestCids(TestCidsInstr* instr) { |
667 | // TODO(sra): Constant fold test. |
668 | SetValue(instr, non_constant_); |
669 | } |
670 | |
671 | void ConstantPropagator::VisitEqualityCompare(EqualityCompareInstr* instr) { |
672 | Definition* left_defn = instr->left()->definition(); |
673 | Definition* right_defn = instr->right()->definition(); |
674 | |
675 | if (IsIntegerClassId(instr->operation_cid())) { |
676 | // Fold x == x, and x != x to true/false for numbers comparisons. |
677 | Definition* unwrapped_left_defn = UnwrapPhi(left_defn); |
678 | Definition* unwrapped_right_defn = UnwrapPhi(right_defn); |
679 | if (unwrapped_left_defn == unwrapped_right_defn) { |
680 | // Fold x === x, and x !== x to true/false. |
681 | if (SetValue(instr, Bool::Get(instr->kind() == Token::kEQ))) { |
682 | if (unwrapped_left_defn != left_defn) { |
683 | MarkUnwrappedPhi(left_defn); |
684 | } |
685 | if (unwrapped_right_defn != right_defn) { |
686 | MarkUnwrappedPhi(right_defn); |
687 | } |
688 | } |
689 | return; |
690 | } |
691 | } |
692 | |
693 | const Object& left = left_defn->constant_value(); |
694 | const Object& right = right_defn->constant_value(); |
695 | if (IsNonConstant(left) || IsNonConstant(right)) { |
696 | SetValue(instr, non_constant_); |
697 | } else if (IsConstant(left) && IsConstant(right)) { |
698 | if (left.IsInteger() && right.IsInteger()) { |
699 | const bool result = CompareIntegers(instr->kind(), Integer::Cast(left), |
700 | Integer::Cast(right)); |
701 | SetValue(instr, Bool::Get(result)); |
702 | } else if (left.IsString() && right.IsString()) { |
703 | const bool result = String::Cast(left).Equals(String::Cast(right)); |
704 | SetValue(instr, Bool::Get((instr->kind() == Token::kEQ) == result)); |
705 | } else { |
706 | SetValue(instr, non_constant_); |
707 | } |
708 | } |
709 | } |
710 | |
711 | void ConstantPropagator::VisitRelationalOp(RelationalOpInstr* instr) { |
712 | const Object& left = instr->left()->definition()->constant_value(); |
713 | const Object& right = instr->right()->definition()->constant_value(); |
714 | if (IsNonConstant(left) || IsNonConstant(right)) { |
715 | SetValue(instr, non_constant_); |
716 | } else if (IsConstant(left) && IsConstant(right)) { |
717 | if (left.IsInteger() && right.IsInteger()) { |
718 | const bool result = CompareIntegers(instr->kind(), Integer::Cast(left), |
719 | Integer::Cast(right)); |
720 | SetValue(instr, Bool::Get(result)); |
721 | } else if (left.IsDouble() && right.IsDouble()) { |
722 | // TODO(srdjan): Implement. |
723 | SetValue(instr, non_constant_); |
724 | } else { |
725 | SetValue(instr, non_constant_); |
726 | } |
727 | } |
728 | } |
729 | |
730 | void ConstantPropagator::VisitNativeCall(NativeCallInstr* instr) { |
731 | SetValue(instr, non_constant_); |
732 | } |
733 | |
734 | void ConstantPropagator::VisitFfiCall(FfiCallInstr* instr) { |
735 | SetValue(instr, non_constant_); |
736 | } |
737 | |
738 | void ConstantPropagator::VisitEnterHandleScope(EnterHandleScopeInstr* instr) { |
739 | SetValue(instr, non_constant_); |
740 | } |
741 | |
742 | void ConstantPropagator::VisitExitHandleScope(ExitHandleScopeInstr* instr) { |
743 | // Nothing to do. |
744 | } |
745 | |
746 | void ConstantPropagator::VisitAllocateHandle(AllocateHandleInstr* instr) { |
747 | SetValue(instr, non_constant_); |
748 | } |
749 | |
750 | void ConstantPropagator::VisitRawStoreField(RawStoreFieldInstr* instr) { |
751 | // Nothing to do. |
752 | } |
753 | |
754 | void ConstantPropagator::VisitDebugStepCheck(DebugStepCheckInstr* instr) { |
755 | // Nothing to do. |
756 | } |
757 | |
758 | void ConstantPropagator::VisitOneByteStringFromCharCode( |
759 | OneByteStringFromCharCodeInstr* instr) { |
760 | const Object& o = instr->char_code()->definition()->constant_value(); |
761 | if (o.IsNull() || IsNonConstant(o)) { |
762 | SetValue(instr, non_constant_); |
763 | } else if (IsConstant(o)) { |
764 | const intptr_t ch_code = Smi::Cast(o).Value(); |
765 | ASSERT(ch_code >= 0); |
766 | if (ch_code < Symbols::kMaxOneCharCodeSymbol) { |
767 | StringPtr* table = Symbols::PredefinedAddress(); |
768 | SetValue(instr, String::ZoneHandle(Z, table[ch_code])); |
769 | } else { |
770 | SetValue(instr, non_constant_); |
771 | } |
772 | } |
773 | } |
774 | |
775 | void ConstantPropagator::VisitStringToCharCode(StringToCharCodeInstr* instr) { |
776 | const Object& o = instr->str()->definition()->constant_value(); |
777 | if (o.IsNull() || IsNonConstant(o)) { |
778 | SetValue(instr, non_constant_); |
779 | } else if (IsConstant(o)) { |
780 | const String& str = String::Cast(o); |
781 | const intptr_t result = |
782 | (str.Length() == 1) ? static_cast<intptr_t>(str.CharAt(0)) : -1; |
783 | SetValue(instr, Smi::ZoneHandle(Z, Smi::New(result))); |
784 | } |
785 | } |
786 | |
787 | void ConstantPropagator::VisitStringInterpolate(StringInterpolateInstr* instr) { |
788 | SetValue(instr, non_constant_); |
789 | } |
790 | |
791 | void ConstantPropagator::VisitUtf8Scan(Utf8ScanInstr* instr) { |
792 | SetValue(instr, non_constant_); |
793 | } |
794 | |
795 | void ConstantPropagator::VisitLoadIndexed(LoadIndexedInstr* instr) { |
796 | const Object& array_obj = instr->array()->definition()->constant_value(); |
797 | const Object& index_obj = instr->index()->definition()->constant_value(); |
798 | if (IsNonConstant(array_obj) || IsNonConstant(index_obj)) { |
799 | SetValue(instr, non_constant_); |
800 | } else if (IsConstant(array_obj) && IsConstant(index_obj)) { |
801 | // Need index to be Smi and array to be either String or an immutable array. |
802 | if (!index_obj.IsSmi()) { |
803 | // Should not occur. |
804 | SetValue(instr, non_constant_); |
805 | return; |
806 | } |
807 | const intptr_t index = Smi::Cast(index_obj).Value(); |
808 | if (index >= 0) { |
809 | if (array_obj.IsString()) { |
810 | const String& str = String::Cast(array_obj); |
811 | if (str.Length() > index) { |
812 | SetValue(instr, |
813 | Smi::Handle( |
814 | Z, Smi::New(static_cast<intptr_t>(str.CharAt(index))))); |
815 | return; |
816 | } |
817 | } else if (array_obj.IsArray()) { |
818 | const Array& a = Array::Cast(array_obj); |
819 | if ((a.Length() > index) && a.IsImmutable()) { |
820 | Instance& result = Instance::Handle(Z); |
821 | result ^= a.At(index); |
822 | SetValue(instr, result); |
823 | return; |
824 | } |
825 | } |
826 | } |
827 | SetValue(instr, non_constant_); |
828 | } |
829 | } |
830 | |
831 | void ConstantPropagator::VisitLoadCodeUnits(LoadCodeUnitsInstr* instr) { |
832 | // TODO(zerny): Implement constant propagation. |
833 | SetValue(instr, non_constant_); |
834 | } |
835 | |
836 | void ConstantPropagator::VisitLoadIndexedUnsafe(LoadIndexedUnsafeInstr* instr) { |
837 | SetValue(instr, non_constant_); |
838 | } |
839 | |
840 | void ConstantPropagator::VisitLoadStaticField(LoadStaticFieldInstr* instr) { |
841 | if (!FLAG_fields_may_be_reset) { |
842 | const Field& field = instr->field(); |
843 | ASSERT(field.is_static()); |
844 | if (field.is_final() && instr->IsFieldInitialized()) { |
845 | Instance& obj = Instance::Handle(Z, field.StaticValue()); |
846 | if (obj.IsSmi() || (obj.IsOld() && obj.IsCanonical())) { |
847 | SetValue(instr, obj); |
848 | return; |
849 | } |
850 | } |
851 | } |
852 | SetValue(instr, non_constant_); |
853 | } |
854 | |
855 | void ConstantPropagator::VisitStoreStaticField(StoreStaticFieldInstr* instr) { |
856 | SetValue(instr, instr->value()->definition()->constant_value()); |
857 | } |
858 | |
859 | void ConstantPropagator::VisitBooleanNegate(BooleanNegateInstr* instr) { |
860 | const Object& value = instr->value()->definition()->constant_value(); |
861 | if (IsNonConstant(value)) { |
862 | SetValue(instr, non_constant_); |
863 | } else if (IsConstant(value)) { |
864 | bool val = value.raw() != Bool::True().raw(); |
865 | SetValue(instr, Bool::Get(val)); |
866 | } |
867 | } |
868 | |
869 | void ConstantPropagator::VisitInstanceOf(InstanceOfInstr* instr) { |
870 | Definition* def = instr->value()->definition(); |
871 | const Object& value = def->constant_value(); |
872 | const AbstractType& checked_type = instr->type(); |
873 | // If the checked type is a top type, the result is always true. |
874 | if (checked_type.IsTopTypeForInstanceOf()) { |
875 | SetValue(instr, Bool::True()); |
876 | } else if (IsNonConstant(value)) { |
877 | intptr_t value_cid = instr->value()->definition()->Type()->ToCid(); |
878 | Representation rep = def->representation(); |
879 | if ((checked_type.IsFloat32x4Type() && (rep == kUnboxedFloat32x4)) || |
880 | (checked_type.IsInt32x4Type() && (rep == kUnboxedInt32x4)) || |
881 | (checked_type.IsDoubleType() && (rep == kUnboxedDouble) && |
882 | FlowGraphCompiler::SupportsUnboxedDoubles()) || |
883 | (checked_type.IsIntType() && (rep == kUnboxedInt64))) { |
884 | // Ensure that compile time type matches representation. |
885 | ASSERT(((rep == kUnboxedFloat32x4) && (value_cid == kFloat32x4Cid)) || |
886 | ((rep == kUnboxedInt32x4) && (value_cid == kInt32x4Cid)) || |
887 | ((rep == kUnboxedDouble) && (value_cid == kDoubleCid)) || |
888 | ((rep == kUnboxedInt64) && (value_cid == kMintCid))); |
889 | // The representation guarantees the type check to be true. |
890 | SetValue(instr, Bool::True()); |
891 | } else { |
892 | SetValue(instr, non_constant_); |
893 | } |
894 | } else if (IsConstant(value)) { |
895 | if (value.IsInstance()) { |
896 | const Instance& instance = Instance::Cast(value); |
897 | if (instr->instantiator_type_arguments()->BindsToConstantNull() && |
898 | instr->function_type_arguments()->BindsToConstantNull()) { |
899 | bool is_instance = |
900 | instance.IsInstanceOf(checked_type, Object::null_type_arguments(), |
901 | Object::null_type_arguments()); |
902 | SetValue(instr, Bool::Get(is_instance)); |
903 | return; |
904 | } |
905 | } |
906 | SetValue(instr, non_constant_); |
907 | } |
908 | } |
909 | |
910 | void ConstantPropagator::VisitCreateArray(CreateArrayInstr* instr) { |
911 | SetValue(instr, non_constant_); |
912 | } |
913 | |
914 | void ConstantPropagator::VisitAllocateObject(AllocateObjectInstr* instr) { |
915 | SetValue(instr, non_constant_); |
916 | } |
917 | |
918 | void ConstantPropagator::VisitLoadUntagged(LoadUntaggedInstr* instr) { |
919 | SetValue(instr, non_constant_); |
920 | } |
921 | |
922 | void ConstantPropagator::VisitLoadClassId(LoadClassIdInstr* instr) { |
923 | // This first part duplicates the work done in LoadClassIdInstr::Canonicalize, |
924 | // which replaces uses of LoadClassIdInstr where the object has a concrete |
925 | // type with a Constant. Canonicalize runs before the ConstantPropagation |
926 | // pass, so if that was all, this wouldn't be needed. |
927 | // |
928 | // However, the ConstantPropagator also runs as part of OptimizeBranches, and |
929 | // TypePropagation runs between it and the previous Canonicalize. Thus, the |
930 | // type may have become concrete and we should take that into account. Not |
931 | // doing so led to some benchmark regressions. |
932 | intptr_t cid = instr->object()->Type()->ToCid(); |
933 | if (cid != kDynamicCid) { |
934 | SetValue(instr, Smi::ZoneHandle(Z, Smi::New(cid))); |
935 | return; |
936 | } |
937 | const Object& object = instr->object()->definition()->constant_value(); |
938 | if (IsConstant(object)) { |
939 | cid = object.GetClassId(); |
940 | SetValue(instr, Smi::ZoneHandle(Z, Smi::New(cid))); |
941 | return; |
942 | } |
943 | SetValue(instr, non_constant_); |
944 | } |
945 | |
946 | void ConstantPropagator::VisitLoadField(LoadFieldInstr* instr) { |
947 | Value* instance = instr->instance(); |
948 | if ((instr->slot().kind() == Slot::Kind::kArray_length) && |
949 | instance->definition()->OriginalDefinition()->IsCreateArray()) { |
950 | Value* num_elements = instance->definition() |
951 | ->OriginalDefinition() |
952 | ->AsCreateArray() |
953 | ->num_elements(); |
954 | if (num_elements->BindsToConstant() && |
955 | num_elements->BoundConstant().IsSmi()) { |
956 | intptr_t length = Smi::Cast(num_elements->BoundConstant()).Value(); |
957 | const Object& result = Smi::ZoneHandle(Z, Smi::New(length)); |
958 | SetValue(instr, result); |
959 | return; |
960 | } |
961 | } |
962 | |
963 | const Object& constant = instance->definition()->constant_value(); |
964 | if (IsConstant(constant)) { |
965 | if (instr->IsImmutableLengthLoad()) { |
966 | if (constant.IsString()) { |
967 | SetValue(instr, |
968 | Smi::ZoneHandle(Z, Smi::New(String::Cast(constant).Length()))); |
969 | return; |
970 | } |
971 | if (constant.IsArray()) { |
972 | SetValue(instr, |
973 | Smi::ZoneHandle(Z, Smi::New(Array::Cast(constant).Length()))); |
974 | return; |
975 | } |
976 | if (constant.IsTypedData()) { |
977 | SetValue(instr, Smi::ZoneHandle( |
978 | Z, Smi::New(TypedData::Cast(constant).Length()))); |
979 | return; |
980 | } |
981 | } else { |
982 | Object& value = Object::Handle(); |
983 | if (instr->Evaluate(constant, &value)) { |
984 | SetValue(instr, Object::ZoneHandle(Z, value.raw())); |
985 | return; |
986 | } |
987 | } |
988 | } |
989 | |
990 | SetValue(instr, non_constant_); |
991 | } |
992 | |
993 | void ConstantPropagator::VisitInstantiateType(InstantiateTypeInstr* instr) { |
994 | TypeArguments& instantiator_type_args = TypeArguments::Handle(Z); |
995 | TypeArguments& function_type_args = TypeArguments::Handle(Z); |
996 | if (!instr->type().IsInstantiated(kCurrentClass)) { |
997 | // Type refers to class type parameters. |
998 | const Object& instantiator_type_args_obj = |
999 | instr->instantiator_type_arguments()->definition()->constant_value(); |
1000 | if (IsNonConstant(instantiator_type_args_obj)) { |
1001 | SetValue(instr, non_constant_); |
1002 | return; |
1003 | } |
1004 | if (IsConstant(instantiator_type_args_obj)) { |
1005 | instantiator_type_args ^= instantiator_type_args_obj.raw(); |
1006 | } else { |
1007 | return; |
1008 | } |
1009 | } |
1010 | if (!instr->type().IsInstantiated(kFunctions)) { |
1011 | // Type refers to function type parameters. |
1012 | const Object& function_type_args_obj = |
1013 | instr->function_type_arguments()->definition()->constant_value(); |
1014 | if (IsNonConstant(function_type_args_obj)) { |
1015 | SetValue(instr, non_constant_); |
1016 | return; |
1017 | } |
1018 | if (IsConstant(function_type_args_obj)) { |
1019 | function_type_args ^= function_type_args_obj.raw(); |
1020 | } else { |
1021 | return; |
1022 | } |
1023 | } |
1024 | AbstractType& result = AbstractType::Handle( |
1025 | Z, instr->type().InstantiateFrom( |
1026 | instantiator_type_args, function_type_args, kAllFree, Heap::kOld)); |
1027 | if (result.IsTypeRef()) { |
1028 | result = TypeRef::Cast(result).type(); |
1029 | } |
1030 | ASSERT(result.IsInstantiated()); |
1031 | result = result.Canonicalize(); |
1032 | SetValue(instr, result); |
1033 | } |
1034 | |
1035 | void ConstantPropagator::VisitInstantiateTypeArguments( |
1036 | InstantiateTypeArgumentsInstr* instr) { |
1037 | TypeArguments& instantiator_type_args = TypeArguments::Handle(Z); |
1038 | TypeArguments& function_type_args = TypeArguments::Handle(Z); |
1039 | if (!instr->type_arguments().IsInstantiated(kCurrentClass)) { |
1040 | // Type arguments refer to class type parameters. |
1041 | const Object& instantiator_type_args_obj = |
1042 | instr->instantiator_type_arguments()->definition()->constant_value(); |
1043 | if (IsNonConstant(instantiator_type_args_obj)) { |
1044 | SetValue(instr, non_constant_); |
1045 | return; |
1046 | } |
1047 | if (IsConstant(instantiator_type_args_obj)) { |
1048 | instantiator_type_args ^= instantiator_type_args_obj.raw(); |
1049 | if (instr->type_arguments().CanShareInstantiatorTypeArguments( |
1050 | instr->instantiator_class())) { |
1051 | SetValue(instr, instantiator_type_args); |
1052 | return; |
1053 | } |
1054 | } else { |
1055 | return; |
1056 | } |
1057 | } |
1058 | if (!instr->type_arguments().IsInstantiated(kFunctions)) { |
1059 | // Type arguments refer to function type parameters. |
1060 | const Object& function_type_args_obj = |
1061 | instr->function_type_arguments()->definition()->constant_value(); |
1062 | if (IsNonConstant(function_type_args_obj)) { |
1063 | SetValue(instr, non_constant_); |
1064 | return; |
1065 | } |
1066 | if (IsConstant(function_type_args_obj)) { |
1067 | function_type_args ^= function_type_args_obj.raw(); |
1068 | if (instr->type_arguments().CanShareFunctionTypeArguments( |
1069 | instr->function())) { |
1070 | SetValue(instr, function_type_args); |
1071 | return; |
1072 | } |
1073 | } else { |
1074 | return; |
1075 | } |
1076 | } |
1077 | TypeArguments& result = TypeArguments::Handle( |
1078 | Z, instr->type_arguments().InstantiateFrom( |
1079 | instantiator_type_args, function_type_args, kAllFree, Heap::kOld)); |
1080 | ASSERT(result.IsInstantiated()); |
1081 | result = result.Canonicalize(); |
1082 | SetValue(instr, result); |
1083 | } |
1084 | |
1085 | void ConstantPropagator::VisitAllocateContext(AllocateContextInstr* instr) { |
1086 | SetValue(instr, non_constant_); |
1087 | } |
1088 | |
1089 | void ConstantPropagator::VisitAllocateUninitializedContext( |
1090 | AllocateUninitializedContextInstr* instr) { |
1091 | SetValue(instr, non_constant_); |
1092 | } |
1093 | |
1094 | void ConstantPropagator::VisitCloneContext(CloneContextInstr* instr) { |
1095 | SetValue(instr, non_constant_); |
1096 | } |
1097 | |
1098 | void ConstantPropagator::VisitBinaryIntegerOp(BinaryIntegerOpInstr* binary_op) { |
1099 | const Object& left = binary_op->left()->definition()->constant_value(); |
1100 | const Object& right = binary_op->right()->definition()->constant_value(); |
1101 | if (IsConstant(left) && IsConstant(right)) { |
1102 | const Integer& result = Integer::Handle( |
1103 | Z, Evaluator::BinaryIntegerEvaluate(left, right, binary_op->op_kind(), |
1104 | binary_op->is_truncating(), |
1105 | binary_op->representation(), T)); |
1106 | if (!result.IsNull()) { |
1107 | SetValue(binary_op, Integer::ZoneHandle(Z, result.raw())); |
1108 | return; |
1109 | } |
1110 | } |
1111 | SetValue(binary_op, non_constant_); |
1112 | } |
1113 | |
1114 | void ConstantPropagator::VisitCheckedSmiOp(CheckedSmiOpInstr* instr) { |
1115 | SetValue(instr, non_constant_); |
1116 | } |
1117 | |
1118 | void ConstantPropagator::VisitCheckedSmiComparison( |
1119 | CheckedSmiComparisonInstr* instr) { |
1120 | SetValue(instr, non_constant_); |
1121 | } |
1122 | |
1123 | void ConstantPropagator::VisitBinarySmiOp(BinarySmiOpInstr* instr) { |
1124 | VisitBinaryIntegerOp(instr); |
1125 | } |
1126 | |
1127 | void ConstantPropagator::VisitBinaryInt32Op(BinaryInt32OpInstr* instr) { |
1128 | VisitBinaryIntegerOp(instr); |
1129 | } |
1130 | |
1131 | void ConstantPropagator::VisitBinaryUint32Op(BinaryUint32OpInstr* instr) { |
1132 | VisitBinaryIntegerOp(instr); |
1133 | } |
1134 | |
1135 | void ConstantPropagator::VisitBinaryInt64Op(BinaryInt64OpInstr* instr) { |
1136 | VisitBinaryIntegerOp(instr); |
1137 | } |
1138 | |
1139 | void ConstantPropagator::VisitShiftInt64Op(ShiftInt64OpInstr* instr) { |
1140 | VisitBinaryIntegerOp(instr); |
1141 | } |
1142 | |
1143 | void ConstantPropagator::VisitSpeculativeShiftInt64Op( |
1144 | SpeculativeShiftInt64OpInstr* instr) { |
1145 | VisitBinaryIntegerOp(instr); |
1146 | } |
1147 | |
1148 | void ConstantPropagator::VisitShiftUint32Op(ShiftUint32OpInstr* instr) { |
1149 | VisitBinaryIntegerOp(instr); |
1150 | } |
1151 | |
1152 | void ConstantPropagator::VisitSpeculativeShiftUint32Op( |
1153 | SpeculativeShiftUint32OpInstr* instr) { |
1154 | VisitBinaryIntegerOp(instr); |
1155 | } |
1156 | |
1157 | void ConstantPropagator::VisitBoxInt64(BoxInt64Instr* instr) { |
1158 | // TODO(kmillikin): Handle box operation. |
1159 | SetValue(instr, non_constant_); |
1160 | } |
1161 | |
1162 | void ConstantPropagator::VisitUnboxInt64(UnboxInt64Instr* instr) { |
1163 | // TODO(kmillikin): Handle unbox operation. |
1164 | SetValue(instr, non_constant_); |
1165 | } |
1166 | |
1167 | void ConstantPropagator::VisitUnaryIntegerOp(UnaryIntegerOpInstr* unary_op) { |
1168 | const Object& value = unary_op->value()->definition()->constant_value(); |
1169 | if (IsConstant(value)) { |
1170 | const Integer& result = Integer::Handle( |
1171 | Z, Evaluator::UnaryIntegerEvaluate(value, unary_op->op_kind(), |
1172 | unary_op->representation(), T)); |
1173 | if (!result.IsNull()) { |
1174 | SetValue(unary_op, Integer::ZoneHandle(Z, result.raw())); |
1175 | return; |
1176 | } |
1177 | } |
1178 | |
1179 | SetValue(unary_op, non_constant_); |
1180 | } |
1181 | |
1182 | void ConstantPropagator::VisitUnaryInt64Op(UnaryInt64OpInstr* instr) { |
1183 | VisitUnaryIntegerOp(instr); |
1184 | } |
1185 | |
1186 | void ConstantPropagator::VisitUnarySmiOp(UnarySmiOpInstr* instr) { |
1187 | VisitUnaryIntegerOp(instr); |
1188 | } |
1189 | |
1190 | void ConstantPropagator::VisitUnaryDoubleOp(UnaryDoubleOpInstr* instr) { |
1191 | const Object& value = instr->value()->definition()->constant_value(); |
1192 | if (IsNonConstant(value)) { |
1193 | SetValue(instr, non_constant_); |
1194 | } else if (IsConstant(value)) { |
1195 | // TODO(kmillikin): Handle unary operations. |
1196 | SetValue(instr, non_constant_); |
1197 | } |
1198 | } |
1199 | |
1200 | void ConstantPropagator::VisitSmiToDouble(SmiToDoubleInstr* instr) { |
1201 | const Object& value = instr->value()->definition()->constant_value(); |
1202 | if (IsConstant(value) && value.IsInteger()) { |
1203 | SetValue(instr, |
1204 | Double::Handle(Z, Double::New(Integer::Cast(value).AsDoubleValue(), |
1205 | Heap::kOld))); |
1206 | } else if (!IsUnknown(value)) { |
1207 | SetValue(instr, non_constant_); |
1208 | } |
1209 | } |
1210 | |
1211 | void ConstantPropagator::VisitInt64ToDouble(Int64ToDoubleInstr* instr) { |
1212 | const Object& value = instr->value()->definition()->constant_value(); |
1213 | if (IsConstant(value) && value.IsInteger()) { |
1214 | SetValue(instr, |
1215 | Double::Handle(Z, Double::New(Integer::Cast(value).AsDoubleValue(), |
1216 | Heap::kOld))); |
1217 | } else if (!IsUnknown(value)) { |
1218 | SetValue(instr, non_constant_); |
1219 | } |
1220 | } |
1221 | |
1222 | void ConstantPropagator::VisitInt32ToDouble(Int32ToDoubleInstr* instr) { |
1223 | const Object& value = instr->value()->definition()->constant_value(); |
1224 | if (IsConstant(value) && value.IsInteger()) { |
1225 | SetValue(instr, |
1226 | Double::Handle(Z, Double::New(Integer::Cast(value).AsDoubleValue(), |
1227 | Heap::kOld))); |
1228 | } else if (!IsUnknown(value)) { |
1229 | SetValue(instr, non_constant_); |
1230 | } |
1231 | } |
1232 | |
1233 | void ConstantPropagator::VisitDoubleToInteger(DoubleToIntegerInstr* instr) { |
1234 | // TODO(kmillikin): Handle conversion. |
1235 | SetValue(instr, non_constant_); |
1236 | } |
1237 | |
1238 | void ConstantPropagator::VisitDoubleToSmi(DoubleToSmiInstr* instr) { |
1239 | // TODO(kmillikin): Handle conversion. |
1240 | SetValue(instr, non_constant_); |
1241 | } |
1242 | |
1243 | void ConstantPropagator::VisitDoubleToDouble(DoubleToDoubleInstr* instr) { |
1244 | // TODO(kmillikin): Handle conversion. |
1245 | SetValue(instr, non_constant_); |
1246 | } |
1247 | |
1248 | void ConstantPropagator::VisitDoubleToFloat(DoubleToFloatInstr* instr) { |
1249 | // TODO(kmillikin): Handle conversion. |
1250 | SetValue(instr, non_constant_); |
1251 | } |
1252 | |
1253 | void ConstantPropagator::VisitFloatToDouble(FloatToDoubleInstr* instr) { |
1254 | // TODO(kmillikin): Handle conversion. |
1255 | SetValue(instr, non_constant_); |
1256 | } |
1257 | |
1258 | void ConstantPropagator::VisitInvokeMathCFunction( |
1259 | InvokeMathCFunctionInstr* instr) { |
1260 | // TODO(kmillikin): Handle conversion. |
1261 | SetValue(instr, non_constant_); |
1262 | } |
1263 | |
1264 | void ConstantPropagator::VisitTruncDivMod(TruncDivModInstr* instr) { |
1265 | // TODO(srdjan): Handle merged instruction. |
1266 | SetValue(instr, non_constant_); |
1267 | } |
1268 | |
1269 | void ConstantPropagator::(ExtractNthOutputInstr* instr) { |
1270 | SetValue(instr, non_constant_); |
1271 | } |
1272 | |
1273 | void ConstantPropagator::VisitConstant(ConstantInstr* instr) { |
1274 | SetValue(instr, instr->value()); |
1275 | } |
1276 | |
1277 | void ConstantPropagator::VisitUnboxedConstant(UnboxedConstantInstr* instr) { |
1278 | SetValue(instr, instr->value()); |
1279 | } |
1280 | |
1281 | void ConstantPropagator::VisitConstraint(ConstraintInstr* instr) { |
1282 | // Should not be used outside of range analysis. |
1283 | UNREACHABLE(); |
1284 | } |
1285 | |
1286 | void ConstantPropagator::VisitMaterializeObject(MaterializeObjectInstr* instr) { |
1287 | // Should not be used outside of allocation elimination pass. |
1288 | UNREACHABLE(); |
1289 | } |
1290 | |
1291 | static bool IsIntegerOrDouble(const Object& value) { |
1292 | return value.IsInteger() || value.IsDouble(); |
1293 | } |
1294 | |
1295 | static double ToDouble(const Object& value) { |
1296 | return value.IsInteger() ? Integer::Cast(value).AsDoubleValue() |
1297 | : Double::Cast(value).value(); |
1298 | } |
1299 | |
1300 | void ConstantPropagator::VisitBinaryDoubleOp(BinaryDoubleOpInstr* instr) { |
1301 | const Object& left = instr->left()->definition()->constant_value(); |
1302 | const Object& right = instr->right()->definition()->constant_value(); |
1303 | if (IsConstant(left) && IsConstant(right)) { |
1304 | const bool both_are_integers = left.IsInteger() && right.IsInteger(); |
1305 | if (IsIntegerOrDouble(left) && IsIntegerOrDouble(right) && |
1306 | !both_are_integers) { |
1307 | const double result_val = Evaluator::EvaluateDoubleOp( |
1308 | ToDouble(left), ToDouble(right), instr->op_kind()); |
1309 | const Double& result = |
1310 | Double::ZoneHandle(Double::NewCanonical(result_val)); |
1311 | SetValue(instr, result); |
1312 | return; |
1313 | } |
1314 | } |
1315 | SetValue(instr, non_constant_); |
1316 | } |
1317 | |
1318 | void ConstantPropagator::VisitDoubleTestOp(DoubleTestOpInstr* instr) { |
1319 | const Object& value = instr->value()->definition()->constant_value(); |
1320 | const bool is_negated = instr->kind() != Token::kEQ; |
1321 | if (value.IsInteger()) { |
1322 | SetValue(instr, is_negated ? Bool::True() : Bool::False()); |
1323 | } else if (IsIntegerOrDouble(value)) { |
1324 | switch (instr->op_kind()) { |
1325 | case MethodRecognizer::kDouble_getIsNaN: { |
1326 | const bool is_nan = isnan(ToDouble(value)); |
1327 | SetValue(instr, Bool::Get(is_negated ? !is_nan : is_nan)); |
1328 | break; |
1329 | } |
1330 | case MethodRecognizer::kDouble_getIsInfinite: { |
1331 | const bool is_inf = isinf(ToDouble(value)); |
1332 | SetValue(instr, Bool::Get(is_negated ? !is_inf : is_inf)); |
1333 | break; |
1334 | } |
1335 | default: |
1336 | UNREACHABLE(); |
1337 | } |
1338 | } else { |
1339 | SetValue(instr, non_constant_); |
1340 | } |
1341 | } |
1342 | |
1343 | void ConstantPropagator::VisitSimdOp(SimdOpInstr* instr) { |
1344 | SetValue(instr, non_constant_); |
1345 | } |
1346 | |
1347 | void ConstantPropagator::VisitMathUnary(MathUnaryInstr* instr) { |
1348 | const Object& value = instr->value()->definition()->constant_value(); |
1349 | if (IsNonConstant(value)) { |
1350 | SetValue(instr, non_constant_); |
1351 | } else if (IsConstant(value)) { |
1352 | // TODO(kmillikin): Handle Math's unary operations (sqrt, cos, sin). |
1353 | SetValue(instr, non_constant_); |
1354 | } |
1355 | } |
1356 | |
1357 | void ConstantPropagator::VisitMathMinMax(MathMinMaxInstr* instr) { |
1358 | const Object& left = instr->left()->definition()->constant_value(); |
1359 | const Object& right = instr->right()->definition()->constant_value(); |
1360 | if (IsNonConstant(left) || IsNonConstant(right)) { |
1361 | SetValue(instr, non_constant_); |
1362 | } else if (IsConstant(left) && IsConstant(right)) { |
1363 | // TODO(srdjan): Handle min and max. |
1364 | SetValue(instr, non_constant_); |
1365 | } |
1366 | } |
1367 | |
1368 | void ConstantPropagator::VisitCaseInsensitiveCompare( |
1369 | CaseInsensitiveCompareInstr* instr) { |
1370 | SetValue(instr, non_constant_); |
1371 | } |
1372 | |
1373 | void ConstantPropagator::VisitUnbox(UnboxInstr* instr) { |
1374 | const Object& value = instr->value()->definition()->constant_value(); |
1375 | if (IsNonConstant(value)) { |
1376 | SetValue(instr, non_constant_); |
1377 | } else if (IsConstant(value)) { |
1378 | // TODO(kmillikin): Handle conversion. |
1379 | SetValue(instr, non_constant_); |
1380 | } |
1381 | } |
1382 | |
1383 | void ConstantPropagator::VisitBox(BoxInstr* instr) { |
1384 | const Object& value = instr->value()->definition()->constant_value(); |
1385 | if (IsNonConstant(value)) { |
1386 | SetValue(instr, non_constant_); |
1387 | } else if (IsConstant(value)) { |
1388 | // TODO(kmillikin): Handle conversion. |
1389 | SetValue(instr, non_constant_); |
1390 | } |
1391 | } |
1392 | |
1393 | void ConstantPropagator::VisitBoxUint32(BoxUint32Instr* instr) { |
1394 | // TODO(kmillikin): Handle box operation. |
1395 | SetValue(instr, non_constant_); |
1396 | } |
1397 | |
1398 | void ConstantPropagator::VisitUnboxUint32(UnboxUint32Instr* instr) { |
1399 | // TODO(kmillikin): Handle unbox operation. |
1400 | SetValue(instr, non_constant_); |
1401 | } |
1402 | |
1403 | void ConstantPropagator::VisitBoxInt32(BoxInt32Instr* instr) { |
1404 | // TODO(kmillikin): Handle box operation. |
1405 | SetValue(instr, non_constant_); |
1406 | } |
1407 | |
1408 | void ConstantPropagator::VisitUnboxInt32(UnboxInt32Instr* instr) { |
1409 | // TODO(kmillikin): Handle unbox operation. |
1410 | SetValue(instr, non_constant_); |
1411 | } |
1412 | |
1413 | void ConstantPropagator::VisitIntConverter(IntConverterInstr* instr) { |
1414 | SetValue(instr, non_constant_); |
1415 | } |
1416 | |
1417 | void ConstantPropagator::VisitBitCast(BitCastInstr* instr) { |
1418 | SetValue(instr, non_constant_); |
1419 | } |
1420 | |
1421 | void ConstantPropagator::VisitUnaryUint32Op(UnaryUint32OpInstr* instr) { |
1422 | // TODO(kmillikin): Handle unary operations. |
1423 | SetValue(instr, non_constant_); |
1424 | } |
1425 | |
1426 | void ConstantPropagator::Analyze() { |
1427 | GraphEntryInstr* entry = graph_->graph_entry(); |
1428 | reachable_->Add(entry->preorder_number()); |
1429 | block_worklist_.Add(entry); |
1430 | |
1431 | while (true) { |
1432 | if (block_worklist_.is_empty()) { |
1433 | if (definition_worklist_.IsEmpty()) break; |
1434 | Definition* definition = definition_worklist_.RemoveLast(); |
1435 | for (Value* use = definition->input_use_list(); use != nullptr; |
1436 | use = use->next_use()) { |
1437 | use->instruction()->Accept(this); |
1438 | } |
1439 | } else { |
1440 | BlockEntryInstr* block = block_worklist_.RemoveLast(); |
1441 | block->Accept(this); |
1442 | } |
1443 | } |
1444 | } |
1445 | |
1446 | static bool HasPhis(BlockEntryInstr* block) { |
1447 | if (auto* join = block->AsJoinEntry()) { |
1448 | return (join->phis() != nullptr) && !join->phis()->is_empty(); |
1449 | } |
1450 | return false; |
1451 | } |
1452 | |
1453 | static bool IsEmptyBlock(BlockEntryInstr* block) { |
1454 | return block->next()->IsGoto() && !HasPhis(block) && |
1455 | !block->IsIndirectEntry(); |
1456 | } |
1457 | |
1458 | // Traverses a chain of empty blocks and returns the first reachable non-empty |
1459 | // block that is not dominated by the start block. The empty blocks are added |
1460 | // to the supplied bit vector. |
1461 | static BlockEntryInstr* FindFirstNonEmptySuccessor(TargetEntryInstr* block, |
1462 | BitVector* empty_blocks) { |
1463 | BlockEntryInstr* current = block; |
1464 | while (IsEmptyBlock(current) && block->Dominates(current)) { |
1465 | ASSERT(!HasPhis(block)); |
1466 | empty_blocks->Add(current->preorder_number()); |
1467 | current = current->next()->AsGoto()->successor(); |
1468 | } |
1469 | return current; |
1470 | } |
1471 | |
1472 | void ConstantPropagator::EliminateRedundantBranches() { |
1473 | // Canonicalize branches that have no side-effects and where true- and |
1474 | // false-targets are the same. |
1475 | bool changed = false; |
1476 | BitVector* empty_blocks = new (Z) BitVector(Z, graph_->preorder().length()); |
1477 | for (BlockIterator b = graph_->postorder_iterator(); !b.Done(); b.Advance()) { |
1478 | BlockEntryInstr* block = b.Current(); |
1479 | BranchInstr* branch = block->last_instruction()->AsBranch(); |
1480 | empty_blocks->Clear(); |
1481 | if ((branch != NULL) && !branch->HasUnknownSideEffects()) { |
1482 | ASSERT(branch->previous() != NULL); // Not already eliminated. |
1483 | BlockEntryInstr* if_true = |
1484 | FindFirstNonEmptySuccessor(branch->true_successor(), empty_blocks); |
1485 | BlockEntryInstr* if_false = |
1486 | FindFirstNonEmptySuccessor(branch->false_successor(), empty_blocks); |
1487 | if (if_true == if_false) { |
1488 | // Replace the branch with a jump to the common successor. |
1489 | // Drop the comparison, which does not have side effects |
1490 | JoinEntryInstr* join = if_true->AsJoinEntry(); |
1491 | if (!HasPhis(join)) { |
1492 | GotoInstr* jump = new (Z) GotoInstr(join, DeoptId::kNone); |
1493 | graph_->CopyDeoptTarget(jump, branch); |
1494 | |
1495 | Instruction* previous = branch->previous(); |
1496 | branch->set_previous(NULL); |
1497 | previous->LinkTo(jump); |
1498 | |
1499 | // Remove uses from branch and all the empty blocks that |
1500 | // are now unreachable. |
1501 | branch->UnuseAllInputs(); |
1502 | for (BitVector::Iterator it(empty_blocks); !it.Done(); it.Advance()) { |
1503 | BlockEntryInstr* empty_block = graph_->preorder()[it.Current()]; |
1504 | empty_block->ClearAllInstructions(); |
1505 | } |
1506 | |
1507 | changed = true; |
1508 | |
1509 | if (FLAG_trace_constant_propagation && graph_->should_print()) { |
1510 | THR_Print("Eliminated branch in B%" Pd " common target B%" Pd "\n" , |
1511 | block->block_id(), join->block_id()); |
1512 | } |
1513 | } |
1514 | } |
1515 | } |
1516 | } |
1517 | |
1518 | if (changed) { |
1519 | graph_->DiscoverBlocks(); |
1520 | graph_->MergeBlocks(); |
1521 | // TODO(fschneider): Update dominator tree in place instead of recomputing. |
1522 | GrowableArray<BitVector*> dominance_frontier; |
1523 | graph_->ComputeDominators(&dominance_frontier); |
1524 | } |
1525 | } |
1526 | |
1527 | void ConstantPropagator::Transform() { |
1528 | // We will recompute dominators, block ordering, block ids, block last |
1529 | // instructions, previous pointers, predecessors, etc. after eliminating |
1530 | // unreachable code. We do not maintain those properties during the |
1531 | // transformation. |
1532 | for (BlockIterator b = graph_->reverse_postorder_iterator(); !b.Done(); |
1533 | b.Advance()) { |
1534 | BlockEntryInstr* block = b.Current(); |
1535 | if (!reachable_->Contains(block->preorder_number())) { |
1536 | if (FLAG_trace_constant_propagation && graph_->should_print()) { |
1537 | THR_Print("Unreachable B%" Pd "\n" , block->block_id()); |
1538 | } |
1539 | // Remove all uses in unreachable blocks. |
1540 | block->ClearAllInstructions(); |
1541 | continue; |
1542 | } |
1543 | |
1544 | JoinEntryInstr* join = block->AsJoinEntry(); |
1545 | if (join != NULL) { |
1546 | // Remove phi inputs corresponding to unreachable predecessor blocks. |
1547 | // Predecessors will be recomputed (in block id order) after removing |
1548 | // unreachable code so we merely have to keep the phi inputs in order. |
1549 | ZoneGrowableArray<PhiInstr*>* phis = join->phis(); |
1550 | if ((phis != NULL) && !phis->is_empty()) { |
1551 | intptr_t pred_count = join->PredecessorCount(); |
1552 | intptr_t live_count = 0; |
1553 | for (intptr_t pred_idx = 0; pred_idx < pred_count; ++pred_idx) { |
1554 | if (reachable_->Contains( |
1555 | join->PredecessorAt(pred_idx)->preorder_number())) { |
1556 | if (live_count < pred_idx) { |
1557 | for (PhiIterator it(join); !it.Done(); it.Advance()) { |
1558 | PhiInstr* phi = it.Current(); |
1559 | ASSERT(phi != NULL); |
1560 | phi->SetInputAt(live_count, phi->InputAt(pred_idx)); |
1561 | } |
1562 | } |
1563 | ++live_count; |
1564 | } else { |
1565 | for (PhiIterator it(join); !it.Done(); it.Advance()) { |
1566 | PhiInstr* phi = it.Current(); |
1567 | ASSERT(phi != NULL); |
1568 | phi->InputAt(pred_idx)->RemoveFromUseList(); |
1569 | } |
1570 | } |
1571 | } |
1572 | if (live_count < pred_count) { |
1573 | intptr_t to_idx = 0; |
1574 | for (intptr_t from_idx = 0; from_idx < phis->length(); ++from_idx) { |
1575 | PhiInstr* phi = (*phis)[from_idx]; |
1576 | ASSERT(phi != NULL); |
1577 | if (FLAG_remove_redundant_phis && (live_count == 1)) { |
1578 | Value* input = phi->InputAt(0); |
1579 | phi->ReplaceUsesWith(input->definition()); |
1580 | input->RemoveFromUseList(); |
1581 | } else { |
1582 | phi->inputs_.TruncateTo(live_count); |
1583 | (*phis)[to_idx++] = phi; |
1584 | } |
1585 | } |
1586 | if (to_idx == 0) { |
1587 | join->phis_ = NULL; |
1588 | } else { |
1589 | phis->TruncateTo(to_idx); |
1590 | } |
1591 | } |
1592 | } |
1593 | } |
1594 | |
1595 | if (join != nullptr) { |
1596 | for (PhiIterator it(join); !it.Done(); it.Advance()) { |
1597 | auto phi = it.Current(); |
1598 | if (TransformDefinition(phi)) { |
1599 | it.RemoveCurrentFromGraph(); |
1600 | } |
1601 | } |
1602 | } |
1603 | for (ForwardInstructionIterator i(block); !i.Done(); i.Advance()) { |
1604 | Definition* defn = i.Current()->AsDefinition(); |
1605 | if (TransformDefinition(defn)) { |
1606 | i.RemoveCurrentFromGraph(); |
1607 | } |
1608 | } |
1609 | |
1610 | // Replace branches where one target is unreachable with jumps. |
1611 | BranchInstr* branch = block->last_instruction()->AsBranch(); |
1612 | if (branch != NULL) { |
1613 | TargetEntryInstr* if_true = branch->true_successor(); |
1614 | TargetEntryInstr* if_false = branch->false_successor(); |
1615 | JoinEntryInstr* join = NULL; |
1616 | Instruction* next = NULL; |
1617 | |
1618 | if (!reachable_->Contains(if_true->preorder_number())) { |
1619 | ASSERT(reachable_->Contains(if_false->preorder_number())); |
1620 | ASSERT(if_false->parallel_move() == NULL); |
1621 | join = new (Z) JoinEntryInstr(if_false->block_id(), |
1622 | if_false->try_index(), DeoptId::kNone); |
1623 | graph_->CopyDeoptTarget(join, if_false); |
1624 | if_false->UnuseAllInputs(); |
1625 | next = if_false->next(); |
1626 | } else if (!reachable_->Contains(if_false->preorder_number())) { |
1627 | ASSERT(if_true->parallel_move() == NULL); |
1628 | join = new (Z) JoinEntryInstr(if_true->block_id(), if_true->try_index(), |
1629 | DeoptId::kNone); |
1630 | graph_->CopyDeoptTarget(join, if_true); |
1631 | if_true->UnuseAllInputs(); |
1632 | next = if_true->next(); |
1633 | } |
1634 | |
1635 | if (join != NULL) { |
1636 | // Replace the branch with a jump to the reachable successor. |
1637 | // Drop the comparison, which does not have side effects as long |
1638 | // as it is a strict compare (the only one we can determine is |
1639 | // constant with the current analysis). |
1640 | GotoInstr* jump = new (Z) GotoInstr(join, DeoptId::kNone); |
1641 | graph_->CopyDeoptTarget(jump, branch); |
1642 | |
1643 | Instruction* previous = branch->previous(); |
1644 | branch->set_previous(NULL); |
1645 | previous->LinkTo(jump); |
1646 | |
1647 | // Replace the false target entry with the new join entry. We will |
1648 | // recompute the dominators after this pass. |
1649 | join->LinkTo(next); |
1650 | branch->UnuseAllInputs(); |
1651 | } |
1652 | } |
1653 | } |
1654 | |
1655 | graph_->DiscoverBlocks(); |
1656 | graph_->MergeBlocks(); |
1657 | GrowableArray<BitVector*> dominance_frontier; |
1658 | graph_->ComputeDominators(&dominance_frontier); |
1659 | } |
1660 | |
1661 | bool ConstantPropagator::TransformDefinition(Definition* defn) { |
1662 | // Replace constant-valued instructions without observable side |
1663 | // effects. Do this for smis and old objects only to avoid having to |
1664 | // copy other objects into the heap's old generation. |
1665 | ASSERT((defn == nullptr) || !defn->IsPushArgument()); |
1666 | if ((defn != nullptr) && IsConstant(defn->constant_value()) && |
1667 | (defn->constant_value().IsSmi() || defn->constant_value().IsOld()) && |
1668 | !defn->IsConstant() && !defn->IsStoreIndexed() && |
1669 | !defn->IsStoreInstanceField() && !defn->IsStoreStaticField()) { |
1670 | if (FLAG_trace_constant_propagation && graph_->should_print()) { |
1671 | THR_Print("Constant v%" Pd " = %s\n" , defn->ssa_temp_index(), |
1672 | defn->constant_value().ToCString()); |
1673 | } |
1674 | constant_value_ = defn->constant_value().raw(); |
1675 | if ((constant_value_.IsString() || constant_value_.IsMint() || |
1676 | constant_value_.IsDouble()) && |
1677 | !constant_value_.IsCanonical()) { |
1678 | const char* error_str = nullptr; |
1679 | constant_value_ = |
1680 | Instance::Cast(constant_value_).CheckAndCanonicalize(T, &error_str); |
1681 | ASSERT(!constant_value_.IsNull() && (error_str == nullptr)); |
1682 | } |
1683 | if (auto call = defn->AsStaticCall()) { |
1684 | ASSERT(!call->HasPushArguments()); |
1685 | } |
1686 | Definition* replacement = |
1687 | graph_->TryCreateConstantReplacementFor(defn, constant_value_); |
1688 | if (replacement != defn) { |
1689 | defn->ReplaceUsesWith(replacement); |
1690 | return true; |
1691 | } |
1692 | } |
1693 | return false; |
1694 | } |
1695 | |
1696 | } // namespace dart |
1697 | |