Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(289)

Side by Side Diff: sdk/lib/_internal/compiler/implementation/ssa/optimize.dart

Issue 694353007: Move dart2js from sdk/lib/_internal/compiler to pkg/compiler (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012, 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 part of ssa;
6
7 abstract class OptimizationPhase {
8 String get name;
9 void visitGraph(HGraph graph);
10 }
11
12 class SsaOptimizerTask extends CompilerTask {
13 final JavaScriptBackend backend;
14 SsaOptimizerTask(JavaScriptBackend backend)
15 : this.backend = backend,
16 super(backend.compiler);
17 String get name => 'SSA optimizer';
18 Compiler get compiler => backend.compiler;
19 Map<HInstruction, Range> ranges = <HInstruction, Range>{};
20
21 void runPhases(HGraph graph, List<OptimizationPhase> phases) {
22 for (OptimizationPhase phase in phases) {
23 runPhase(graph, phase);
24 }
25 }
26
27 void runPhase(HGraph graph, OptimizationPhase phase) {
28 phase.visitGraph(graph);
29 compiler.tracer.traceGraph(phase.name, graph);
30 assert(graph.isValid());
31 }
32
33 void optimize(CodegenWorkItem work, HGraph graph) {
34 ConstantSystem constantSystem = compiler.backend.constantSystem;
35 JavaScriptItemCompilationContext context = work.compilationContext;
36 measure(() {
37 SsaDeadCodeEliminator dce;
38 List<OptimizationPhase> phases = <OptimizationPhase>[
39 // Run trivial instruction simplification first to optimize
40 // some patterns useful for type conversion.
41 new SsaInstructionSimplifier(constantSystem, backend, work),
42 new SsaTypeConversionInserter(compiler),
43 new SsaRedundantPhiEliminator(),
44 new SsaDeadPhiEliminator(),
45 new SsaTypePropagator(compiler),
46 // After type propagation, more instructions can be
47 // simplified.
48 new SsaInstructionSimplifier(constantSystem, backend, work),
49 new SsaCheckInserter(backend, work, context.boundsChecked),
50 new SsaInstructionSimplifier(constantSystem, backend, work),
51 new SsaCheckInserter(backend, work, context.boundsChecked),
52 new SsaTypePropagator(compiler),
53 // Run a dead code eliminator before LICM because dead
54 // interceptors are often in the way of LICM'able instructions.
55 new SsaDeadCodeEliminator(compiler),
56 new SsaGlobalValueNumberer(compiler),
57 // After GVN, some instructions might need their type to be
58 // updated because they now have different inputs.
59 new SsaTypePropagator(compiler),
60 new SsaCodeMotion(),
61 new SsaLoadElimination(compiler),
62 new SsaDeadPhiEliminator(),
63 new SsaTypePropagator(compiler),
64 new SsaValueRangeAnalyzer(compiler, constantSystem, work),
65 // Previous optimizations may have generated new
66 // opportunities for instruction simplification.
67 new SsaInstructionSimplifier(constantSystem, backend, work),
68 new SsaCheckInserter(backend, work, context.boundsChecked),
69 new SsaSimplifyInterceptors(compiler, constantSystem, work),
70 dce = new SsaDeadCodeEliminator(compiler),
71 new SsaTypePropagator(compiler)];
72 runPhases(graph, phases);
73 if (dce.eliminatedSideEffects) {
74 phases = <OptimizationPhase>[
75 new SsaGlobalValueNumberer(compiler),
76 new SsaCodeMotion(),
77 new SsaValueRangeAnalyzer(compiler, constantSystem, work),
78 new SsaInstructionSimplifier(constantSystem, backend, work),
79 new SsaCheckInserter(backend, work, context.boundsChecked),
80 new SsaSimplifyInterceptors(compiler, constantSystem, work),
81 new SsaDeadCodeEliminator(compiler)];
82 } else {
83 phases = <OptimizationPhase>[
84 // Run the simplifier to remove unneeded type checks inserted
85 // by type propagation.
86 new SsaInstructionSimplifier(constantSystem, backend, work)];
87 }
88 runPhases(graph, phases);
89 });
90 }
91 }
92
93 bool isFixedLength(mask, Compiler compiler) {
94 ClassWorld classWorld = compiler.world;
95 JavaScriptBackend backend = compiler.backend;
96 if (mask.isContainer && mask.length != null) {
97 // A container on which we have inferred the length.
98 return true;
99 } else if (mask.containsOnly(backend.jsFixedArrayClass)
100 || mask.containsOnlyString(classWorld)
101 || backend.isTypedArray(mask)) {
102 return true;
103 }
104 return false;
105 }
106
107 /**
108 * If both inputs to known operations are available execute the operation at
109 * compile-time.
110 */
111 class SsaInstructionSimplifier extends HBaseVisitor
112 implements OptimizationPhase {
113
114 // We don't produce constant-folded strings longer than this unless they have
115 // a single use. This protects against exponentially large constant folded
116 // strings.
117 static const MAX_SHARED_CONSTANT_FOLDED_STRING_LENGTH = 512;
118
119 final String name = "SsaInstructionSimplifier";
120 final JavaScriptBackend backend;
121 final CodegenWorkItem work;
122 final ConstantSystem constantSystem;
123 HGraph graph;
124 Compiler get compiler => backend.compiler;
125
126 SsaInstructionSimplifier(this.constantSystem, this.backend, this.work);
127
128 void visitGraph(HGraph visitee) {
129 graph = visitee;
130 visitDominatorTree(visitee);
131 }
132
133 visitBasicBlock(HBasicBlock block) {
134 HInstruction instruction = block.first;
135 while (instruction != null) {
136 HInstruction next = instruction.next;
137 HInstruction replacement = instruction.accept(this);
138 if (replacement != instruction) {
139 block.rewrite(instruction, replacement);
140
141 // The intersection of double and int return conflicting, and
142 // because of our number implementation for JavaScript, it
143 // might be that an operation thought to return double, can be
144 // simplified to an int. For example:
145 // `2.5 * 10`.
146 if (!(replacement.isNumberOrNull(compiler)
147 && instruction.isNumberOrNull(compiler))) {
148 // If we can replace [instruction] with [replacement], then
149 // [replacement]'s type can be narrowed.
150 TypeMask newType = replacement.instructionType.intersection(
151 instruction.instructionType, compiler.world);
152 replacement.instructionType = newType;
153 }
154
155 // If the replacement instruction does not know its
156 // source element, use the source element of the
157 // instruction.
158 if (replacement.sourceElement == null) {
159 replacement.sourceElement = instruction.sourceElement;
160 }
161 if (replacement.sourcePosition == null) {
162 replacement.sourcePosition = instruction.sourcePosition;
163 }
164 if (!replacement.isInBasicBlock()) {
165 // The constant folding can return an instruction that is already
166 // part of the graph (like an input), so we only add the replacement
167 // if necessary.
168 block.addAfter(instruction, replacement);
169 // Visit the replacement as the next instruction in case it
170 // can also be constant folded away.
171 next = replacement;
172 }
173 block.remove(instruction);
174 }
175 instruction = next;
176 }
177 }
178
179 HInstruction visitInstruction(HInstruction node) {
180 return node;
181 }
182
183 HInstruction visitBoolify(HBoolify node) {
184 List<HInstruction> inputs = node.inputs;
185 assert(inputs.length == 1);
186 HInstruction input = inputs[0];
187 if (input.isBoolean(compiler)) return input;
188 // All values that cannot be 'true' are boolified to false.
189 TypeMask mask = input.instructionType;
190 if (!mask.contains(backend.jsBoolClass, compiler.world)) {
191 return graph.addConstantBool(false, compiler);
192 }
193 return node;
194 }
195
196 HInstruction visitNot(HNot node) {
197 List<HInstruction> inputs = node.inputs;
198 assert(inputs.length == 1);
199 HInstruction input = inputs[0];
200 if (input is HConstant) {
201 HConstant constant = input;
202 bool isTrue = constant.constant.isTrue;
203 return graph.addConstantBool(!isTrue, compiler);
204 } else if (input is HNot) {
205 return input.inputs[0];
206 }
207 return node;
208 }
209
210 HInstruction visitInvokeUnary(HInvokeUnary node) {
211 HInstruction folded =
212 foldUnary(node.operation(constantSystem), node.operand);
213 return folded != null ? folded : node;
214 }
215
216 HInstruction foldUnary(UnaryOperation operation, HInstruction operand) {
217 if (operand is HConstant) {
218 HConstant receiver = operand;
219 ConstantValue folded = operation.fold(receiver.constant);
220 if (folded != null) return graph.addConstant(folded, compiler);
221 }
222 return null;
223 }
224
225 HInstruction tryOptimizeLengthInterceptedGetter(HInvokeDynamic node) {
226 HInstruction actualReceiver = node.inputs[1];
227 if (actualReceiver.isIndexablePrimitive(compiler)) {
228 if (actualReceiver.isConstantString()) {
229 HConstant constantInput = actualReceiver;
230 StringConstantValue constant = constantInput.constant;
231 return graph.addConstantInt(constant.length, compiler);
232 } else if (actualReceiver.isConstantList()) {
233 HConstant constantInput = actualReceiver;
234 ListConstantValue constant = constantInput.constant;
235 return graph.addConstantInt(constant.length, compiler);
236 }
237 Element element = backend.jsIndexableLength;
238 bool isFixed = isFixedLength(actualReceiver.instructionType, compiler);
239 HFieldGet result = new HFieldGet(
240 element, actualReceiver, backend.positiveIntType,
241 isAssignable: !isFixed);
242 return result;
243 } else if (actualReceiver.isConstantMap()) {
244 HConstant constantInput = actualReceiver;
245 MapConstantValue constant = constantInput.constant;
246 return graph.addConstantInt(constant.length, compiler);
247 }
248 return null;
249 }
250
251 HInstruction handleInterceptedCall(HInvokeDynamic node) {
252 // Try constant folding the instruction.
253 Operation operation = node.specializer.operation(constantSystem);
254 if (operation != null) {
255 HInstruction instruction = node.inputs.length == 2
256 ? foldUnary(operation, node.inputs[1])
257 : foldBinary(operation, node.inputs[1], node.inputs[2]);
258 if (instruction != null) return instruction;
259 }
260
261 // Try converting the instruction to a builtin instruction.
262 HInstruction instruction =
263 node.specializer.tryConvertToBuiltin(node, compiler);
264 if (instruction != null) return instruction;
265
266 Selector selector = node.selector;
267 HInstruction input = node.inputs[1];
268
269 World world = compiler.world;
270 if (selector.isCall || selector.isOperator) {
271 Element target;
272 if (input.isExtendableArray(compiler)) {
273 if (selector.applies(backend.jsArrayRemoveLast, world)) {
274 target = backend.jsArrayRemoveLast;
275 } else if (selector.applies(backend.jsArrayAdd, world)) {
276 // The codegen special cases array calls, but does not
277 // inline argument type checks.
278 if (!compiler.enableTypeAssertions) {
279 target = backend.jsArrayAdd;
280 }
281 }
282 } else if (input.isStringOrNull(compiler)) {
283 if (selector.applies(backend.jsStringSplit, world)) {
284 HInstruction argument = node.inputs[2];
285 if (argument.isString(compiler)) {
286 target = backend.jsStringSplit;
287 }
288 } else if (selector.applies(backend.jsStringOperatorAdd, world)) {
289 // `operator+` is turned into a JavaScript '+' so we need to
290 // make sure the receiver and the argument are not null.
291 // TODO(sra): Do this via [node.specializer].
292 HInstruction argument = node.inputs[2];
293 if (argument.isString(compiler)
294 && !input.canBeNull()) {
295 return new HStringConcat(input, argument, null,
296 node.instructionType);
297 }
298 } else if (selector.applies(backend.jsStringToString, world)
299 && !input.canBeNull()) {
300 return input;
301 }
302 }
303 if (target != null) {
304 // TODO(ngeoffray): There is a strong dependency between codegen
305 // and this optimization that the dynamic invoke does not need an
306 // interceptor. We currently need to keep a
307 // HInvokeDynamicMethod and not create a HForeign because
308 // HForeign is too opaque for the SsaCheckInserter (that adds a
309 // bounds check on removeLast). Once we start inlining, the
310 // bounds check will become explicit, so we won't need this
311 // optimization.
312 HInvokeDynamicMethod result = new HInvokeDynamicMethod(
313 node.selector, node.inputs.sublist(1), node.instructionType);
314 result.element = target;
315 return result;
316 }
317 } else if (selector.isGetter) {
318 if (selector.asUntyped.applies(backend.jsIndexableLength, world)) {
319 HInstruction optimized = tryOptimizeLengthInterceptedGetter(node);
320 if (optimized != null) return optimized;
321 }
322 }
323
324 return node;
325 }
326
327 HInstruction visitInvokeDynamicMethod(HInvokeDynamicMethod node) {
328 if (node.isInterceptedCall) {
329 HInstruction folded = handleInterceptedCall(node);
330 if (folded != node) return folded;
331 }
332
333 TypeMask receiverType = node.getDartReceiver(compiler).instructionType;
334 Selector selector =
335 new TypedSelector(receiverType, node.selector, compiler.world);
336 Element element = compiler.world.locateSingleElement(selector);
337 // TODO(ngeoffray): Also fold if it's a getter or variable.
338 if (element != null
339 && element.isFunction
340 // If we found out that the only target is a [:noSuchMethod:],
341 // we just ignore it.
342 && element.name == selector.name) {
343 FunctionElement method = element;
344
345 if (method.isNative) {
346 HInstruction folded = tryInlineNativeMethod(node, method);
347 if (folded != null) return folded;
348 } else {
349 // TODO(ngeoffray): If the method has optional parameters,
350 // we should pass the default values.
351 FunctionSignature parameters = method.functionSignature;
352 if (parameters.optionalParameterCount == 0
353 || parameters.parameterCount == node.selector.argumentCount) {
354 node.element = element;
355 }
356 }
357 }
358 return node;
359 }
360
361 HInstruction tryInlineNativeMethod(HInvokeDynamicMethod node,
362 FunctionElement method) {
363 // Enable direct calls to a native method only if we don't run in checked
364 // mode, where the Dart version may have type annotations on parameters and
365 // return type that it should check.
366 // Also check that the parameters are not functions: it's the callee that
367 // will translate them to JS functions.
368 //
369 // TODO(ngeoffray): There are some cases where we could still inline in
370 // checked mode if we know the arguments have the right type. And we could
371 // do the closure conversion as well as the return type annotation check.
372
373 if (!node.isInterceptedCall) return null;
374
375 // TODO(sra): Check for legacy methods with bodies in the native strings.
376 // foo() native 'return something';
377 // They should not be used.
378
379 FunctionSignature signature = method.functionSignature;
380 if (signature.optionalParametersAreNamed) return null;
381
382 // Return types on native methods don't need to be checked, since the
383 // declaration has to be truthful.
384
385 // The call site might omit optional arguments. The inlined code must
386 // preserve the number of arguments, so check only the actual arguments.
387
388 List<HInstruction> inputs = node.inputs.sublist(1);
389 int inputPosition = 1; // Skip receiver.
390 bool canInline = true;
391 signature.forEachParameter((ParameterElement element) {
392 if (inputPosition < inputs.length && canInline) {
393 HInstruction input = inputs[inputPosition++];
394 DartType type = element.type.unalias(compiler);
395 if (type is FunctionType) {
396 canInline = false;
397 }
398 if (compiler.enableTypeAssertions) {
399 // TODO(sra): Check if [input] is guaranteed to pass the parameter
400 // type check. Consider using a strengthened type check to avoid
401 // passing `null` to primitive types since the native methods usually
402 // have non-nullable primitive parameter types.
403 canInline = false;
404 }
405 }
406 });
407
408 if (!canInline) return null;
409
410 // Strengthen instruction type from annotations to help optimize
411 // dependent instructions.
412 native.NativeBehavior nativeBehavior =
413 native.NativeBehavior.ofMethod(method, compiler);
414 TypeMask returnType =
415 TypeMaskFactory.fromNativeBehavior(nativeBehavior, compiler);
416 HInvokeDynamicMethod result =
417 new HInvokeDynamicMethod(node.selector, inputs, returnType);
418 result.element = method;
419 return result;
420 }
421
422 HInstruction visitBoundsCheck(HBoundsCheck node) {
423 HInstruction index = node.index;
424 if (index.isInteger(compiler)) return node;
425 if (index.isConstant()) {
426 HConstant constantInstruction = index;
427 assert(!constantInstruction.constant.isInt);
428 if (!constantSystem.isInt(constantInstruction.constant)) {
429 // -0.0 is a double but will pass the runtime integer check.
430 node.staticChecks = HBoundsCheck.ALWAYS_FALSE;
431 }
432 }
433 return node;
434 }
435
436 HInstruction foldBinary(BinaryOperation operation,
437 HInstruction left,
438 HInstruction right) {
439 if (left is HConstant && right is HConstant) {
440 HConstant op1 = left;
441 HConstant op2 = right;
442 ConstantValue folded = operation.fold(op1.constant, op2.constant);
443 if (folded != null) return graph.addConstant(folded, compiler);
444 }
445 return null;
446 }
447
448 HInstruction visitAdd(HAdd node) {
449 HInstruction left = node.left;
450 HInstruction right = node.right;
451 // We can only perform this rewriting on Integer, as it is not
452 // valid for -0.0.
453 if (left.isInteger(compiler) && right.isInteger(compiler)) {
454 if (left is HConstant && left.constant.isZero) return right;
455 if (right is HConstant && right.constant.isZero) return left;
456 }
457 return super.visitAdd(node);
458 }
459
460 HInstruction visitMultiply(HMultiply node) {
461 HInstruction left = node.left;
462 HInstruction right = node.right;
463 if (left.isNumber(compiler) && right.isNumber(compiler)) {
464 if (left is HConstant && left.constant.isOne) return right;
465 if (right is HConstant && right.constant.isOne) return left;
466 }
467 return super.visitMultiply(node);
468 }
469
470 HInstruction visitInvokeBinary(HInvokeBinary node) {
471 HInstruction left = node.left;
472 HInstruction right = node.right;
473 BinaryOperation operation = node.operation(constantSystem);
474 HConstant folded = foldBinary(operation, left, right);
475 if (folded != null) return folded;
476 return node;
477 }
478
479 bool allUsersAreBoolifies(HInstruction instruction) {
480 List<HInstruction> users = instruction.usedBy;
481 int length = users.length;
482 for (int i = 0; i < length; i++) {
483 if (users[i] is! HBoolify) return false;
484 }
485 return true;
486 }
487
488 HInstruction visitRelational(HRelational node) {
489 if (allUsersAreBoolifies(node)) {
490 // TODO(ngeoffray): Call a boolified selector.
491 // This node stays the same, but the Boolify node will go away.
492 }
493 // Note that we still have to call [super] to make sure that we end up
494 // in the remaining optimizations.
495 return super.visitRelational(node);
496 }
497
498 HInstruction handleIdentityCheck(HRelational node) {
499 HInstruction left = node.left;
500 HInstruction right = node.right;
501 TypeMask leftType = left.instructionType;
502 TypeMask rightType = right.instructionType;
503
504 // Intersection of int and double return conflicting, so
505 // we don't optimize on numbers to preserve the runtime semantics.
506 if (!(left.isNumberOrNull(compiler) && right.isNumberOrNull(compiler))) {
507 TypeMask intersection = leftType.intersection(rightType, compiler.world);
508 if (intersection.isEmpty && !intersection.isNullable) {
509 return graph.addConstantBool(false, compiler);
510 }
511 }
512
513 if (left.isNull() && right.isNull()) {
514 return graph.addConstantBool(true, compiler);
515 }
516
517 if (left.isConstantBoolean() && right.isBoolean(compiler)) {
518 HConstant constant = left;
519 if (constant.constant.isTrue) {
520 return right;
521 } else {
522 return new HNot(right, backend.boolType);
523 }
524 }
525
526 if (right.isConstantBoolean() && left.isBoolean(compiler)) {
527 HConstant constant = right;
528 if (constant.constant.isTrue) {
529 return left;
530 } else {
531 return new HNot(left, backend.boolType);
532 }
533 }
534
535 return null;
536 }
537
538 HInstruction visitIdentity(HIdentity node) {
539 HInstruction newInstruction = handleIdentityCheck(node);
540 return newInstruction == null ? super.visitIdentity(node) : newInstruction;
541 }
542
543 void simplifyCondition(HBasicBlock block,
544 HInstruction condition,
545 bool value) {
546 condition.dominatedUsers(block.first).forEach((user) {
547 HInstruction newCondition = graph.addConstantBool(value, compiler);
548 user.changeUse(condition, newCondition);
549 });
550 }
551
552 HInstruction visitIf(HIf node) {
553 HInstruction condition = node.condition;
554 if (condition.isConstant()) return node;
555 bool isNegated = condition is HNot;
556
557 if (isNegated) {
558 condition = condition.inputs[0];
559 } else {
560 // It is possible for LICM to move a negated version of the
561 // condition out of the loop where it used. We still want to
562 // simplify the nested use of the condition in that case, so
563 // we look for all dominating negated conditions and replace
564 // nested uses of them with true or false.
565 Iterable<HInstruction> dominating = condition.usedBy.where((user) =>
566 user is HNot && user.dominates(node));
567 dominating.forEach((hoisted) {
568 simplifyCondition(node.thenBlock, hoisted, false);
569 simplifyCondition(node.elseBlock, hoisted, true);
570 });
571 }
572 simplifyCondition(node.thenBlock, condition, !isNegated);
573 simplifyCondition(node.elseBlock, condition, isNegated);
574 return node;
575 }
576
577 HInstruction visitIs(HIs node) {
578 DartType type = node.typeExpression;
579 Element element = type.element;
580
581 if (!node.isRawCheck) {
582 return node;
583 } else if (type.isTypedef) {
584 return node;
585 } else if (element == compiler.functionClass) {
586 return node;
587 }
588
589 if (element == compiler.objectClass || type.treatAsDynamic) {
590 return graph.addConstantBool(true, compiler);
591 }
592
593 ClassWorld classWorld = compiler.world;
594 HInstruction expression = node.expression;
595 if (expression.isInteger(compiler)) {
596 if (identical(element, compiler.intClass)
597 || identical(element, compiler.numClass)
598 || Elements.isNumberOrStringSupertype(element, compiler)) {
599 return graph.addConstantBool(true, compiler);
600 } else if (identical(element, compiler.doubleClass)) {
601 // We let the JS semantics decide for that check. Currently
602 // the code we emit will always return true.
603 return node;
604 } else {
605 return graph.addConstantBool(false, compiler);
606 }
607 } else if (expression.isDouble(compiler)) {
608 if (identical(element, compiler.doubleClass)
609 || identical(element, compiler.numClass)
610 || Elements.isNumberOrStringSupertype(element, compiler)) {
611 return graph.addConstantBool(true, compiler);
612 } else if (identical(element, compiler.intClass)) {
613 // We let the JS semantics decide for that check. Currently
614 // the code we emit will return true for a double that can be
615 // represented as a 31-bit integer and for -0.0.
616 return node;
617 } else {
618 return graph.addConstantBool(false, compiler);
619 }
620 } else if (expression.isNumber(compiler)) {
621 if (identical(element, compiler.numClass)) {
622 return graph.addConstantBool(true, compiler);
623 } else {
624 // We cannot just return false, because the expression may be of
625 // type int or double.
626 }
627 } else if (expression.canBePrimitiveNumber(compiler)
628 && identical(element, compiler.intClass)) {
629 // We let the JS semantics decide for that check.
630 return node;
631 // We need the [:hasTypeArguments:] check because we don't have
632 // the notion of generics in the backend. For example, [:this:] in
633 // a class [:A<T>:], is currently always considered to have the
634 // raw type.
635 } else if (!RuntimeTypes.hasTypeArguments(type)) {
636 TypeMask expressionMask = expression.instructionType;
637 assert(TypeMask.assertIsNormalized(expressionMask, classWorld));
638 TypeMask typeMask = (element == compiler.nullClass)
639 ? new TypeMask.subtype(element, classWorld)
640 : new TypeMask.nonNullSubtype(element, classWorld);
641 if (expressionMask.union(typeMask, classWorld) == typeMask) {
642 return graph.addConstantBool(true, compiler);
643 } else if (expressionMask.intersection(typeMask,
644 compiler.world).isEmpty) {
645 return graph.addConstantBool(false, compiler);
646 }
647 }
648 return node;
649 }
650
651 HInstruction visitTypeConversion(HTypeConversion node) {
652 HInstruction value = node.inputs[0];
653 DartType type = node.typeExpression;
654 if (type != null) {
655 if (type.isMalformed) {
656 // Malformed types are treated as dynamic statically, but should
657 // throw a type error at runtime.
658 return node;
659 }
660 if (!type.treatAsRaw || type.isTypeVariable) {
661 return node;
662 }
663 if (type.isFunctionType) {
664 // TODO(johnniwinther): Optimize function type conversions.
665 return node;
666 }
667 }
668 return removeIfCheckAlwaysSucceeds(node, node.checkedType);
669 }
670
671 HInstruction visitTypeKnown(HTypeKnown node) {
672 return removeIfCheckAlwaysSucceeds(node, node.knownType);
673 }
674
675 HInstruction removeIfCheckAlwaysSucceeds(HCheck node, TypeMask checkedType) {
676 ClassWorld classWorld = compiler.world;
677 if (checkedType.containsAll(classWorld)) return node;
678 HInstruction input = node.checkedInput;
679 TypeMask inputType = input.instructionType;
680 return inputType.isInMask(checkedType, classWorld) ? input : node;
681 }
682
683 VariableElement findConcreteFieldForDynamicAccess(HInstruction receiver,
684 Selector selector) {
685 TypeMask receiverType = receiver.instructionType;
686 return compiler.world.locateSingleField(
687 new TypedSelector(receiverType, selector, compiler.world));
688 }
689
690 HInstruction visitFieldGet(HFieldGet node) {
691 if (node.isNullCheck) return node;
692 var receiver = node.receiver;
693 if (node.element == backend.jsIndexableLength) {
694 JavaScriptItemCompilationContext context = work.compilationContext;
695 if (context.allocatedFixedLists.contains(receiver)) {
696 // TODO(ngeoffray): checking if the second input is an integer
697 // should not be necessary but it currently makes it easier for
698 // other optimizations to reason about a fixed length constructor
699 // that we know takes an int.
700 if (receiver.inputs[0].isInteger(compiler)) {
701 return receiver.inputs[0];
702 }
703 } else if (receiver.isConstantList() || receiver.isConstantString()) {
704 return graph.addConstantInt(receiver.constant.length, compiler);
705 } else {
706 var type = receiver.instructionType;
707 if (type.isContainer && type.length != null) {
708 HInstruction constant = graph.addConstantInt(type.length, compiler);
709 if (type.isNullable) {
710 // If the container can be null, we update all uses of the
711 // length access to use the constant instead, but keep the
712 // length access in the graph, to ensure we still have a
713 // null check.
714 node.block.rewrite(node, constant);
715 return node;
716 } else {
717 return constant;
718 }
719 }
720 }
721 }
722
723 // HFieldGet of a constructed constant can be replaced with the constant's
724 // field.
725 if (receiver is HConstant) {
726 ConstantValue constant = receiver.constant;
727 if (constant.isConstructedObject) {
728 ConstructedConstantValue constructedConstant = constant;
729 Map<Element, ConstantValue> fields = constructedConstant.fieldElements;
730 ConstantValue value = fields[node.element];
731 if (value != null) {
732 return graph.addConstant(value, compiler);
733 }
734 }
735 }
736
737 return node;
738 }
739
740 HInstruction visitIndex(HIndex node) {
741 if (node.receiver.isConstantList() && node.index.isConstantInteger()) {
742 var instruction = node.receiver;
743 List<ConstantValue> entries = instruction.constant.entries;
744 instruction = node.index;
745 int index = instruction.constant.primitiveValue;
746 if (index >= 0 && index < entries.length) {
747 return graph.addConstant(entries[index], compiler);
748 }
749 }
750 return node;
751 }
752
753 HInstruction visitInvokeDynamicGetter(HInvokeDynamicGetter node) {
754 if (node.isInterceptedCall) {
755 HInstruction folded = handleInterceptedCall(node);
756 if (folded != node) return folded;
757 }
758 HInstruction receiver = node.getDartReceiver(compiler);
759 Element field = findConcreteFieldForDynamicAccess(receiver, node.selector);
760 if (field == null) return node;
761 return directFieldGet(receiver, field);
762 }
763
764 HInstruction directFieldGet(HInstruction receiver, Element field) {
765 bool isAssignable = !compiler.world.fieldNeverChanges(field);
766
767 TypeMask type;
768 if (field.enclosingClass.isNative) {
769 type = TypeMaskFactory.fromNativeBehavior(
770 native.NativeBehavior.ofFieldLoad(field, compiler),
771 compiler);
772 } else {
773 type = TypeMaskFactory.inferredTypeForElement(field, compiler);
774 }
775
776 return new HFieldGet(
777 field, receiver, type, isAssignable: isAssignable);
778 }
779
780 HInstruction visitInvokeDynamicSetter(HInvokeDynamicSetter node) {
781 if (node.isInterceptedCall) {
782 HInstruction folded = handleInterceptedCall(node);
783 if (folded != node) return folded;
784 }
785
786 HInstruction receiver = node.getDartReceiver(compiler);
787 VariableElement field =
788 findConcreteFieldForDynamicAccess(receiver, node.selector);
789 if (field == null || !field.isAssignable) return node;
790 // Use [:node.inputs.last:] in case the call follows the
791 // interceptor calling convention, but is not a call on an
792 // interceptor.
793 HInstruction value = node.inputs.last;
794 if (compiler.enableTypeAssertions) {
795 DartType type = field.type;
796 if (!type.treatAsRaw || type.isTypeVariable) {
797 // We cannot generate the correct type representation here, so don't
798 // inline this access.
799 return node;
800 }
801 HInstruction other = value.convertType(
802 compiler,
803 type,
804 HTypeConversion.CHECKED_MODE_CHECK);
805 if (other != value) {
806 node.block.addBefore(node, other);
807 value = other;
808 }
809 }
810 return new HFieldSet(field, receiver, value);
811 }
812
813 HInstruction visitStringConcat(HStringConcat node) {
814 // Simplify string concat:
815 //
816 // "" + R -> R
817 // L + "" -> L
818 // "L" + "R" -> "LR"
819 // (prefix + "L") + "R" -> prefix + "LR"
820 //
821 StringConstantValue getString(HInstruction instruction) {
822 if (!instruction.isConstantString()) return null;
823 HConstant constant = instruction;
824 return constant.constant;
825 }
826
827 StringConstantValue leftString = getString(node.left);
828 if (leftString != null && leftString.primitiveValue.length == 0) {
829 return node.right;
830 }
831
832 StringConstantValue rightString = getString(node.right);
833 if (rightString == null) return node;
834 if (rightString.primitiveValue.length == 0) return node.left;
835
836 HInstruction prefix = null;
837 if (leftString == null) {
838 if (node.left is! HStringConcat) return node;
839 HStringConcat leftConcat = node.left;
840 // Don't undo CSE.
841 if (leftConcat.usedBy.length != 1) return node;
842 prefix = leftConcat.left;
843 leftString = getString(leftConcat.right);
844 if (leftString == null) return node;
845 }
846
847 if (leftString.primitiveValue.length + rightString.primitiveValue.length >
848 MAX_SHARED_CONSTANT_FOLDED_STRING_LENGTH) {
849 if (node.usedBy.length > 1) return node;
850 }
851
852 HInstruction folded = graph.addConstant(
853 constantSystem.createString(new ast.DartString.concat(
854 leftString.primitiveValue, rightString.primitiveValue)),
855 compiler);
856 if (prefix == null) return folded;
857 return new HStringConcat(prefix, folded, node.node, backend.stringType);
858 }
859
860 HInstruction visitStringify(HStringify node) {
861 HInstruction input = node.inputs[0];
862 if (input.isString(compiler)) return input;
863 if (input.isConstant()) {
864 HConstant constant = input;
865 if (!constant.constant.isPrimitive) return node;
866 if (constant.constant.isInt) {
867 // Only constant-fold int.toString() when Dart and JS results the same.
868 // TODO(18103): We should be able to remove this work-around when issue
869 // 18103 is resolved by providing the correct string.
870 IntConstantValue intConstant = constant.constant;
871 // Very conservative range.
872 if (!intConstant.isUInt32()) return node;
873 }
874 PrimitiveConstantValue primitive = constant.constant;
875 return graph.addConstant(constantSystem.createString(
876 primitive.toDartString()), compiler);
877 }
878 return node;
879 }
880
881 HInstruction visitOneShotInterceptor(HOneShotInterceptor node) {
882 return handleInterceptedCall(node);
883 }
884 }
885
886 class SsaCheckInserter extends HBaseVisitor implements OptimizationPhase {
887 final Set<HInstruction> boundsChecked;
888 final CodegenWorkItem work;
889 final JavaScriptBackend backend;
890 final String name = "SsaCheckInserter";
891 HGraph graph;
892
893 SsaCheckInserter(this.backend,
894 this.work,
895 this.boundsChecked);
896
897 void visitGraph(HGraph graph) {
898 this.graph = graph;
899 visitDominatorTree(graph);
900 }
901
902 void visitBasicBlock(HBasicBlock block) {
903 HInstruction instruction = block.first;
904 while (instruction != null) {
905 HInstruction next = instruction.next;
906 instruction = instruction.accept(this);
907 instruction = next;
908 }
909 }
910
911 HBoundsCheck insertBoundsCheck(HInstruction indexNode,
912 HInstruction array,
913 HInstruction indexArgument) {
914 Compiler compiler = backend.compiler;
915 HFieldGet length = new HFieldGet(
916 backend.jsIndexableLength, array, backend.positiveIntType,
917 isAssignable: !isFixedLength(array.instructionType, compiler));
918 indexNode.block.addBefore(indexNode, length);
919
920 TypeMask type = indexArgument.isPositiveInteger(compiler)
921 ? indexArgument.instructionType
922 : backend.positiveIntType;
923 HBoundsCheck check = new HBoundsCheck(
924 indexArgument, length, array, type);
925 indexNode.block.addBefore(indexNode, check);
926 // If the index input to the bounds check was not known to be an integer
927 // then we replace its uses with the bounds check, which is known to be an
928 // integer. However, if the input was already an integer we don't do this
929 // because putting in a check instruction might obscure the real nature of
930 // the index eg. if it is a constant. The range information from the
931 // BoundsCheck instruction is attached to the input directly by
932 // visitBoundsCheck in the SsaValueRangeAnalyzer.
933 if (!indexArgument.isInteger(compiler)) {
934 indexArgument.replaceAllUsersDominatedBy(indexNode, check);
935 }
936 boundsChecked.add(indexNode);
937 return check;
938 }
939
940 void visitIndex(HIndex node) {
941 if (boundsChecked.contains(node)) return;
942 HInstruction index = node.index;
943 index = insertBoundsCheck(node, node.receiver, index);
944 }
945
946 void visitIndexAssign(HIndexAssign node) {
947 if (boundsChecked.contains(node)) return;
948 HInstruction index = node.index;
949 index = insertBoundsCheck(node, node.receiver, index);
950 }
951
952 void visitInvokeDynamicMethod(HInvokeDynamicMethod node) {
953 Element element = node.element;
954 if (node.isInterceptedCall) return;
955 if (element != backend.jsArrayRemoveLast) return;
956 if (boundsChecked.contains(node)) return;
957 insertBoundsCheck(
958 node, node.receiver, graph.addConstantInt(0, backend.compiler));
959 }
960 }
961
962 class SsaDeadCodeEliminator extends HGraphVisitor implements OptimizationPhase {
963 final String name = "SsaDeadCodeEliminator";
964
965 final Compiler compiler;
966 SsaLiveBlockAnalyzer analyzer;
967 bool eliminatedSideEffects = false;
968 SsaDeadCodeEliminator(this.compiler);
969
970 HInstruction zapInstructionCache;
971 HInstruction get zapInstruction {
972 if (zapInstructionCache == null) {
973 // A constant with no type does not pollute types at phi nodes.
974 ConstantValue constant =
975 new DummyConstantValue(const TypeMask.nonNullEmpty());
976 zapInstructionCache = analyzer.graph.addConstant(constant, compiler);
977 }
978 return zapInstructionCache;
979 }
980
981 /// Returns whether the next throwing instruction that may have side
982 /// effects after [instruction], throws [NoSuchMethodError] on the
983 /// same receiver of [instruction].
984 bool hasFollowingThrowingNSM(HInstruction instruction) {
985 HInstruction receiver = instruction.getDartReceiver(compiler);
986 HInstruction current = instruction.next;
987 do {
988 if ((current.getDartReceiver(compiler) == receiver)
989 && current.canThrow()) {
990 return true;
991 }
992 if (current.canThrow() || current.sideEffects.hasSideEffects()) {
993 return false;
994 }
995 if (current.next == null && current is HGoto) {
996 // We do not merge blocks in our SSA graph, so if this block
997 // just jumps to a single predecessor, visit this predecessor.
998 assert(current.block.successors.length == 1);
999 current = current.block.successors[0].first;
1000 } else {
1001 current = current.next;
1002 }
1003 } while (current != null);
1004 return false;
1005 }
1006
1007 bool isDeadCode(HInstruction instruction) {
1008 if (!instruction.usedBy.isEmpty) return false;
1009 if (instruction.sideEffects.hasSideEffects()) return false;
1010 if (instruction.canThrow()
1011 && instruction.onlyThrowsNSM()
1012 && hasFollowingThrowingNSM(instruction)) {
1013 return true;
1014 }
1015 return !instruction.canThrow()
1016 && instruction is !HParameterValue
1017 && instruction is !HLocalSet;
1018 }
1019
1020 void visitGraph(HGraph graph) {
1021 analyzer = new SsaLiveBlockAnalyzer(graph, compiler);
1022 analyzer.analyze();
1023 visitPostDominatorTree(graph);
1024 cleanPhis(graph);
1025 }
1026
1027 void visitBasicBlock(HBasicBlock block) {
1028 bool isDeadBlock = analyzer.isDeadBlock(block);
1029 block.isLive = !isDeadBlock;
1030 // Start from the last non-control flow instruction in the block.
1031 HInstruction instruction = block.last.previous;
1032 while (instruction != null) {
1033 var previous = instruction.previous;
1034 if (isDeadBlock) {
1035 eliminatedSideEffects = eliminatedSideEffects ||
1036 instruction.sideEffects.hasSideEffects();
1037 removeUsers(instruction);
1038 block.remove(instruction);
1039 } else if (isDeadCode(instruction)) {
1040 block.remove(instruction);
1041 }
1042 instruction = previous;
1043 }
1044 }
1045
1046 void cleanPhis(HGraph graph) {
1047 L: for (HBasicBlock block in graph.blocks) {
1048 List<HBasicBlock> predecessors = block.predecessors;
1049 // Zap all inputs to phis that correspond to dead blocks.
1050 block.forEachPhi((HPhi phi) {
1051 for (int i = 0; i < phi.inputs.length; ++i) {
1052 if (!predecessors[i].isLive && phi.inputs[i] != zapInstruction) {
1053 replaceInput(i, phi, zapInstruction);
1054 }
1055 }
1056 });
1057 if (predecessors.length < 2) continue L;
1058 // Find the index of the single live predecessor if it exists.
1059 int indexOfLive = -1;
1060 for (int i = 0; i < predecessors.length; i++) {
1061 if (predecessors[i].isLive) {
1062 if (indexOfLive >= 0) continue L;
1063 indexOfLive = i;
1064 }
1065 }
1066 // Run through the phis of the block and replace them with their input
1067 // that comes from the only live predecessor if that dominates the phi.
1068 block.forEachPhi((HPhi phi) {
1069 HInstruction replacement = (indexOfLive >= 0)
1070 ? phi.inputs[indexOfLive] : zapInstruction;
1071 if (replacement.dominates(phi)) {
1072 block.rewrite(phi, replacement);
1073 block.removePhi(phi);
1074 }
1075 });
1076 }
1077 }
1078
1079 void replaceInput(int i, HInstruction from, HInstruction by) {
1080 from.inputs[i].usedBy.remove(from);
1081 from.inputs[i] = by;
1082 by.usedBy.add(from);
1083 }
1084
1085 void removeUsers(HInstruction instruction) {
1086 instruction.usedBy.forEach((user) {
1087 removeInput(user, instruction);
1088 });
1089 instruction.usedBy.clear();
1090 }
1091
1092 void removeInput(HInstruction user, HInstruction input) {
1093 List<HInstruction> inputs = user.inputs;
1094 for (int i = 0, length = inputs.length; i < length; i++) {
1095 if (input == inputs[i]) {
1096 user.inputs[i] = zapInstruction;
1097 zapInstruction.usedBy.add(user);
1098 }
1099 }
1100 }
1101 }
1102
1103 class SsaLiveBlockAnalyzer extends HBaseVisitor {
1104 final HGraph graph;
1105 final Compiler compiler;
1106 final Set<HBasicBlock> live = new Set<HBasicBlock>();
1107 final List<HBasicBlock> worklist = <HBasicBlock>[];
1108
1109 SsaLiveBlockAnalyzer(this.graph, this.compiler);
1110
1111 JavaScriptBackend get backend => compiler.backend;
1112 Map<HInstruction, Range> get ranges => backend.optimizer.ranges;
1113
1114 bool isDeadBlock(HBasicBlock block) => !live.contains(block);
1115
1116 void analyze() {
1117 markBlockLive(graph.entry);
1118 while (!worklist.isEmpty) {
1119 HBasicBlock live = worklist.removeLast();
1120 live.last.accept(this);
1121 }
1122 }
1123
1124 void markBlockLive(HBasicBlock block) {
1125 if (!live.contains(block)) {
1126 worklist.add(block);
1127 live.add(block);
1128 }
1129 }
1130
1131 void visitControlFlow(HControlFlow instruction) {
1132 instruction.block.successors.forEach(markBlockLive);
1133 }
1134
1135 void visitIf(HIf instruction) {
1136 HInstruction condition = instruction.condition;
1137 if (condition.isConstant()) {
1138 if (condition.isConstantTrue()) {
1139 markBlockLive(instruction.thenBlock);
1140 } else {
1141 markBlockLive(instruction.elseBlock);
1142 }
1143 } else {
1144 visitControlFlow(instruction);
1145 }
1146 }
1147
1148 void visitSwitch(HSwitch node) {
1149 if (node.expression.isInteger(compiler)) {
1150 Range switchRange = ranges[node.expression];
1151 if (switchRange != null &&
1152 switchRange.lower is IntValue &&
1153 switchRange.upper is IntValue) {
1154 IntValue lowerValue = switchRange.lower;
1155 IntValue upperValue = switchRange.upper;
1156 int lower = lowerValue.value;
1157 int upper = upperValue.value;
1158 Set<int> liveLabels = new Set<int>();
1159 for (int pos = 1; pos < node.inputs.length; pos++) {
1160 HConstant input = node.inputs[pos];
1161 if (!input.isConstantInteger()) continue;
1162 IntConstantValue constant = input.constant;
1163 int label = constant.primitiveValue;
1164 if (!liveLabels.contains(label) &&
1165 label <= upper &&
1166 label >= lower) {
1167 markBlockLive(node.block.successors[pos - 1]);
1168 liveLabels.add(label);
1169 }
1170 }
1171 if (liveLabels.length != upper - lower + 1) {
1172 markBlockLive(node.defaultTarget);
1173 }
1174 return;
1175 }
1176 }
1177 visitControlFlow(node);
1178 }
1179 }
1180
1181 class SsaDeadPhiEliminator implements OptimizationPhase {
1182 final String name = "SsaDeadPhiEliminator";
1183
1184 void visitGraph(HGraph graph) {
1185 final List<HPhi> worklist = <HPhi>[];
1186 // A set to keep track of the live phis that we found.
1187 final Set<HPhi> livePhis = new Set<HPhi>();
1188
1189 // Add to the worklist all live phis: phis referenced by non-phi
1190 // instructions.
1191 for (final block in graph.blocks) {
1192 block.forEachPhi((HPhi phi) {
1193 for (final user in phi.usedBy) {
1194 if (user is !HPhi) {
1195 worklist.add(phi);
1196 livePhis.add(phi);
1197 break;
1198 }
1199 }
1200 });
1201 }
1202
1203 // Process the worklist by propagating liveness to phi inputs.
1204 while (!worklist.isEmpty) {
1205 HPhi phi = worklist.removeLast();
1206 for (final input in phi.inputs) {
1207 if (input is HPhi && !livePhis.contains(input)) {
1208 worklist.add(input);
1209 livePhis.add(input);
1210 }
1211 }
1212 }
1213
1214 // Remove phis that are not live.
1215 // Traverse in reverse order to remove phis with no uses before the
1216 // phis that they might use.
1217 // NOTICE: Doesn't handle circular references, but we don't currently
1218 // create any.
1219 List<HBasicBlock> blocks = graph.blocks;
1220 for (int i = blocks.length - 1; i >= 0; i--) {
1221 HBasicBlock block = blocks[i];
1222 HPhi current = block.phis.first;
1223 HPhi next = null;
1224 while (current != null) {
1225 next = current.next;
1226 if (!livePhis.contains(current)
1227 // TODO(ahe): Not sure the following is correct.
1228 && current.usedBy.isEmpty) {
1229 block.removePhi(current);
1230 }
1231 current = next;
1232 }
1233 }
1234 }
1235 }
1236
1237 class SsaRedundantPhiEliminator implements OptimizationPhase {
1238 final String name = "SsaRedundantPhiEliminator";
1239
1240 void visitGraph(HGraph graph) {
1241 final List<HPhi> worklist = <HPhi>[];
1242
1243 // Add all phis in the worklist.
1244 for (final block in graph.blocks) {
1245 block.forEachPhi((HPhi phi) => worklist.add(phi));
1246 }
1247
1248 while (!worklist.isEmpty) {
1249 HPhi phi = worklist.removeLast();
1250
1251 // If the phi has already been processed, continue.
1252 if (!phi.isInBasicBlock()) continue;
1253
1254 // Find if the inputs of the phi are the same instruction.
1255 // The builder ensures that phi.inputs[0] cannot be the phi
1256 // itself.
1257 assert(!identical(phi.inputs[0], phi));
1258 HInstruction candidate = phi.inputs[0];
1259 for (int i = 1; i < phi.inputs.length; i++) {
1260 HInstruction input = phi.inputs[i];
1261 // If the input is the phi, the phi is still candidate for
1262 // elimination.
1263 if (!identical(input, candidate) && !identical(input, phi)) {
1264 candidate = null;
1265 break;
1266 }
1267 }
1268
1269 // If the inputs are not the same, continue.
1270 if (candidate == null) continue;
1271
1272 // Because we're updating the users of this phi, we may have new
1273 // phis candidate for elimination. Add phis that used this phi
1274 // to the worklist.
1275 for (final user in phi.usedBy) {
1276 if (user is HPhi) worklist.add(user);
1277 }
1278 phi.block.rewrite(phi, candidate);
1279 phi.block.removePhi(phi);
1280 }
1281 }
1282 }
1283
1284 class GvnWorkItem {
1285 final HBasicBlock block;
1286 final ValueSet valueSet;
1287 GvnWorkItem(this.block, this.valueSet);
1288 }
1289
1290 class SsaGlobalValueNumberer implements OptimizationPhase {
1291 final String name = "SsaGlobalValueNumberer";
1292 final Compiler compiler;
1293 final Set<int> visited;
1294
1295 List<int> blockChangesFlags;
1296 List<int> loopChangesFlags;
1297
1298 SsaGlobalValueNumberer(this.compiler) : visited = new Set<int>();
1299
1300 void visitGraph(HGraph graph) {
1301 computeChangesFlags(graph);
1302 moveLoopInvariantCode(graph);
1303 List<GvnWorkItem> workQueue =
1304 <GvnWorkItem>[new GvnWorkItem(graph.entry, new ValueSet())];
1305 do {
1306 GvnWorkItem item = workQueue.removeLast();
1307 visitBasicBlock(item.block, item.valueSet, workQueue);
1308 } while (!workQueue.isEmpty);
1309 }
1310
1311 void moveLoopInvariantCode(HGraph graph) {
1312 for (int i = graph.blocks.length - 1; i >= 0; i--) {
1313 HBasicBlock block = graph.blocks[i];
1314 if (block.isLoopHeader()) {
1315 int changesFlags = loopChangesFlags[block.id];
1316 HLoopInformation info = block.loopInformation;
1317 // Iterate over all blocks of this loop. Note that blocks in
1318 // inner loops are not visited here, but we know they
1319 // were visited before because we are iterating in post-order.
1320 // So instructions that are GVN'ed in an inner loop are in their
1321 // loop entry, and [info.blocks] contains this loop entry.
1322 moveLoopInvariantCodeFromBlock(block, block, changesFlags);
1323 for (HBasicBlock other in info.blocks) {
1324 moveLoopInvariantCodeFromBlock(other, block, changesFlags);
1325 }
1326 }
1327 }
1328 }
1329
1330 void moveLoopInvariantCodeFromBlock(HBasicBlock block,
1331 HBasicBlock loopHeader,
1332 int changesFlags) {
1333 assert(block.parentLoopHeader == loopHeader || block == loopHeader);
1334 HBasicBlock preheader = loopHeader.predecessors[0];
1335 int dependsFlags = SideEffects.computeDependsOnFlags(changesFlags);
1336 HInstruction instruction = block.first;
1337 bool isLoopAlwaysTaken() {
1338 HInstruction instruction = loopHeader.last;
1339 assert(instruction is HGoto || instruction is HLoopBranch);
1340 return instruction is HGoto
1341 || instruction.inputs[0].isConstantTrue();
1342 }
1343 bool firstInstructionInLoop = block == loopHeader
1344 // Compensate for lack of code motion.
1345 || (blockChangesFlags[loopHeader.id] == 0
1346 && isLoopAlwaysTaken()
1347 && loopHeader.successors[0] == block);
1348 while (instruction != null) {
1349 HInstruction next = instruction.next;
1350 if (instruction.useGvn() && instruction.isMovable
1351 && (!instruction.canThrow() || firstInstructionInLoop)
1352 && !instruction.sideEffects.dependsOn(dependsFlags)) {
1353 bool loopInvariantInputs = true;
1354 List<HInstruction> inputs = instruction.inputs;
1355 for (int i = 0, length = inputs.length; i < length; i++) {
1356 if (isInputDefinedAfterDominator(inputs[i], preheader)) {
1357 loopInvariantInputs = false;
1358 break;
1359 }
1360 }
1361
1362 // If the inputs are loop invariant, we can move the
1363 // instruction from the current block to the pre-header block.
1364 if (loopInvariantInputs) {
1365 block.detach(instruction);
1366 preheader.moveAtExit(instruction);
1367 } else {
1368 firstInstructionInLoop = false;
1369 }
1370 }
1371 int oldChangesFlags = changesFlags;
1372 changesFlags |= instruction.sideEffects.getChangesFlags();
1373 if (oldChangesFlags != changesFlags) {
1374 dependsFlags = SideEffects.computeDependsOnFlags(changesFlags);
1375 }
1376 instruction = next;
1377 }
1378 }
1379
1380 bool isInputDefinedAfterDominator(HInstruction input,
1381 HBasicBlock dominator) {
1382 return input.block.id > dominator.id;
1383 }
1384
1385 void visitBasicBlock(
1386 HBasicBlock block, ValueSet values, List<GvnWorkItem> workQueue) {
1387 HInstruction instruction = block.first;
1388 if (block.isLoopHeader()) {
1389 int flags = loopChangesFlags[block.id];
1390 values.kill(flags);
1391 }
1392 while (instruction != null) {
1393 HInstruction next = instruction.next;
1394 int flags = instruction.sideEffects.getChangesFlags();
1395 assert(flags == 0 || !instruction.useGvn());
1396 values.kill(flags);
1397 if (instruction.useGvn()) {
1398 HInstruction other = values.lookup(instruction);
1399 if (other != null) {
1400 assert(other.gvnEquals(instruction) && instruction.gvnEquals(other));
1401 block.rewriteWithBetterUser(instruction, other);
1402 block.remove(instruction);
1403 } else {
1404 values.add(instruction);
1405 }
1406 }
1407 instruction = next;
1408 }
1409
1410 List<HBasicBlock> dominatedBlocks = block.dominatedBlocks;
1411 for (int i = 0, length = dominatedBlocks.length; i < length; i++) {
1412 HBasicBlock dominated = dominatedBlocks[i];
1413 // No need to copy the value set for the last child.
1414 ValueSet successorValues = (i == length - 1) ? values : values.copy();
1415 // If we have no values in our set, we do not have to kill
1416 // anything. Also, if the range of block ids from the current
1417 // block to the dominated block is empty, there is no blocks on
1418 // any path from the current block to the dominated block so we
1419 // don't have to do anything either.
1420 assert(block.id < dominated.id);
1421 if (!successorValues.isEmpty && block.id + 1 < dominated.id) {
1422 visited.clear();
1423 List<HBasicBlock> workQueue = <HBasicBlock>[dominated];
1424 int changesFlags = 0;
1425 do {
1426 HBasicBlock current = workQueue.removeLast();
1427 changesFlags |=
1428 getChangesFlagsForDominatedBlock(block, current, workQueue);
1429 } while (!workQueue.isEmpty);
1430 successorValues.kill(changesFlags);
1431 }
1432 workQueue.add(new GvnWorkItem(dominated, successorValues));
1433 }
1434 }
1435
1436 void computeChangesFlags(HGraph graph) {
1437 // Create the changes flags lists. Make sure to initialize the
1438 // loop changes flags list to zero so we can use bitwise or when
1439 // propagating loop changes upwards.
1440 final int length = graph.blocks.length;
1441 blockChangesFlags = new List<int>(length);
1442 loopChangesFlags = new List<int>(length);
1443 for (int i = 0; i < length; i++) loopChangesFlags[i] = 0;
1444
1445 // Run through all the basic blocks in the graph and fill in the
1446 // changes flags lists.
1447 for (int i = length - 1; i >= 0; i--) {
1448 final HBasicBlock block = graph.blocks[i];
1449 final int id = block.id;
1450
1451 // Compute block changes flags for the block.
1452 int changesFlags = 0;
1453 HInstruction instruction = block.first;
1454 while (instruction != null) {
1455 changesFlags |= instruction.sideEffects.getChangesFlags();
1456 instruction = instruction.next;
1457 }
1458 assert(blockChangesFlags[id] == null);
1459 blockChangesFlags[id] = changesFlags;
1460
1461 // Loop headers are part of their loop, so update the loop
1462 // changes flags accordingly.
1463 if (block.isLoopHeader()) {
1464 loopChangesFlags[id] |= changesFlags;
1465 }
1466
1467 // Propagate loop changes flags upwards.
1468 HBasicBlock parentLoopHeader = block.parentLoopHeader;
1469 if (parentLoopHeader != null) {
1470 loopChangesFlags[parentLoopHeader.id] |= (block.isLoopHeader())
1471 ? loopChangesFlags[id]
1472 : changesFlags;
1473 }
1474 }
1475 }
1476
1477 int getChangesFlagsForDominatedBlock(HBasicBlock dominator,
1478 HBasicBlock dominated,
1479 List<HBasicBlock> workQueue) {
1480 int changesFlags = 0;
1481 List<HBasicBlock> predecessors = dominated.predecessors;
1482 for (int i = 0, length = predecessors.length; i < length; i++) {
1483 HBasicBlock block = predecessors[i];
1484 int id = block.id;
1485 // If the current predecessor block is on the path from the
1486 // dominator to the dominated, it must have an id that is in the
1487 // range from the dominator to the dominated.
1488 if (dominator.id < id && id < dominated.id && !visited.contains(id)) {
1489 visited.add(id);
1490 changesFlags |= blockChangesFlags[id];
1491 // Loop bodies might not be on the path from dominator to dominated,
1492 // but they can invalidate values.
1493 changesFlags |= loopChangesFlags[id];
1494 workQueue.add(block);
1495 }
1496 }
1497 return changesFlags;
1498 }
1499 }
1500
1501 // This phase merges equivalent instructions on different paths into
1502 // one instruction in a dominator block. It runs through the graph
1503 // post dominator order and computes a ValueSet for each block of
1504 // instructions that can be moved to a dominator block. These
1505 // instructions are the ones that:
1506 // 1) can be used for GVN, and
1507 // 2) do not use definitions of their own block.
1508 //
1509 // A basic block looks at its sucessors and finds the intersection of
1510 // these computed ValueSet. It moves all instructions of the
1511 // intersection into its own list of instructions.
1512 class SsaCodeMotion extends HBaseVisitor implements OptimizationPhase {
1513 final String name = "SsaCodeMotion";
1514
1515 List<ValueSet> values;
1516
1517 void visitGraph(HGraph graph) {
1518 values = new List<ValueSet>(graph.blocks.length);
1519 for (int i = 0; i < graph.blocks.length; i++) {
1520 values[graph.blocks[i].id] = new ValueSet();
1521 }
1522 visitPostDominatorTree(graph);
1523 }
1524
1525 void visitBasicBlock(HBasicBlock block) {
1526 List<HBasicBlock> successors = block.successors;
1527
1528 // Phase 1: get the ValueSet of all successors (if there are more than one),
1529 // compute the intersection and move the instructions of the intersection
1530 // into this block.
1531 if (successors.length > 1) {
1532 ValueSet instructions = values[successors[0].id];
1533 for (int i = 1; i < successors.length; i++) {
1534 ValueSet other = values[successors[i].id];
1535 instructions = instructions.intersection(other);
1536 }
1537
1538 if (!instructions.isEmpty) {
1539 List<HInstruction> list = instructions.toList();
1540 for (HInstruction instruction in list) {
1541 // Move the instruction to the current block.
1542 instruction.block.detach(instruction);
1543 block.moveAtExit(instruction);
1544 // Go through all successors and rewrite their instruction
1545 // to the shared one.
1546 for (final successor in successors) {
1547 HInstruction toRewrite = values[successor.id].lookup(instruction);
1548 if (toRewrite != instruction) {
1549 successor.rewriteWithBetterUser(toRewrite, instruction);
1550 successor.remove(toRewrite);
1551 }
1552 }
1553 }
1554 }
1555 }
1556
1557 // Don't try to merge instructions to a dominator if we have
1558 // multiple predecessors.
1559 if (block.predecessors.length != 1) return;
1560
1561 // Phase 2: Go through all instructions of this block and find
1562 // which instructions can be moved to a dominator block.
1563 ValueSet set_ = values[block.id];
1564 HInstruction instruction = block.first;
1565 int flags = 0;
1566 while (instruction != null) {
1567 int dependsFlags = SideEffects.computeDependsOnFlags(flags);
1568 flags |= instruction.sideEffects.getChangesFlags();
1569
1570 HInstruction current = instruction;
1571 instruction = instruction.next;
1572 if (!current.useGvn() || !current.isMovable) continue;
1573 // TODO(sra): We could move throwing instructions provided we keep the
1574 // exceptions in the same order. This requires they are in the same order
1575 // in all successors, which is not tracked by the ValueSet.
1576 if (current.canThrow()) continue;
1577 if (current.sideEffects.dependsOn(dependsFlags)) continue;
1578
1579 bool canBeMoved = true;
1580 for (final HInstruction input in current.inputs) {
1581 if (input.block == block) {
1582 canBeMoved = false;
1583 break;
1584 }
1585 }
1586 if (!canBeMoved) continue;
1587
1588 HInstruction existing = set_.lookup(current);
1589 if (existing == null) {
1590 set_.add(current);
1591 } else {
1592 block.rewriteWithBetterUser(current, existing);
1593 block.remove(current);
1594 }
1595 }
1596 }
1597 }
1598
1599 class SsaTypeConversionInserter extends HBaseVisitor
1600 implements OptimizationPhase {
1601 final String name = "SsaTypeconversionInserter";
1602 final Compiler compiler;
1603
1604 SsaTypeConversionInserter(this.compiler);
1605
1606 void visitGraph(HGraph graph) {
1607 visitDominatorTree(graph);
1608 }
1609
1610 // Update users of [input] that are dominated by [:dominator.first:]
1611 // to use [TypeKnown] of [input] instead. As the type information depends
1612 // on the control flow, we mark the inserted [HTypeKnown] nodes as
1613 // non-movable.
1614 void insertTypePropagationForDominatedUsers(HBasicBlock dominator,
1615 HInstruction input,
1616 TypeMask convertedType) {
1617 Setlet<HInstruction> dominatedUsers = input.dominatedUsers(dominator.first);
1618 if (dominatedUsers.isEmpty) return;
1619
1620 HTypeKnown newInput = new HTypeKnown.pinned(convertedType, input);
1621 dominator.addBefore(dominator.first, newInput);
1622 dominatedUsers.forEach((HInstruction user) {
1623 user.changeUse(input, newInput);
1624 });
1625 }
1626
1627 void visitIs(HIs instruction) {
1628 DartType type = instruction.typeExpression;
1629 Element element = type.element;
1630 if (!instruction.isRawCheck) {
1631 return;
1632 } else if (element.isTypedef) {
1633 return;
1634 }
1635
1636 List<HInstruction> ifUsers = <HInstruction>[];
1637 List<HInstruction> notIfUsers = <HInstruction>[];
1638
1639 collectIfUsers(instruction, ifUsers, notIfUsers);
1640
1641 if (ifUsers.isEmpty && notIfUsers.isEmpty) return;
1642
1643 TypeMask convertedType =
1644 new TypeMask.nonNullSubtype(element, compiler.world);
1645 HInstruction input = instruction.expression;
1646
1647 for (HIf ifUser in ifUsers) {
1648 insertTypePropagationForDominatedUsers(ifUser.thenBlock, input,
1649 convertedType);
1650 // TODO(ngeoffray): Also change uses for the else block on a type
1651 // that knows it is not of a specific type.
1652 }
1653 for (HIf ifUser in notIfUsers) {
1654 insertTypePropagationForDominatedUsers(ifUser.elseBlock, input,
1655 convertedType);
1656 // TODO(ngeoffray): Also change uses for the then block on a type
1657 // that knows it is not of a specific type.
1658 }
1659 }
1660
1661 void visitIdentity(HIdentity instruction) {
1662 // At HIf(HIdentity(x, null)) strengthens x to non-null on else branch.
1663 HInstruction left = instruction.left;
1664 HInstruction right = instruction.right;
1665 HInstruction input;
1666
1667 if (left.isConstantNull()) {
1668 input = right;
1669 } else if (right.isConstantNull()) {
1670 input = left;
1671 } else {
1672 return;
1673 }
1674
1675 if (!input.instructionType.isNullable) return;
1676
1677 List<HInstruction> ifUsers = <HInstruction>[];
1678 List<HInstruction> notIfUsers = <HInstruction>[];
1679
1680 collectIfUsers(instruction, ifUsers, notIfUsers);
1681
1682 if (ifUsers.isEmpty && notIfUsers.isEmpty) return;
1683
1684 TypeMask nonNullType = input.instructionType.nonNullable();
1685
1686 for (HIf ifUser in ifUsers) {
1687 insertTypePropagationForDominatedUsers(ifUser.elseBlock, input,
1688 nonNullType);
1689 // Uses in thenBlock are `null`, but probably not common.
1690 }
1691 for (HIf ifUser in notIfUsers) {
1692 insertTypePropagationForDominatedUsers(ifUser.thenBlock, input,
1693 nonNullType);
1694 // Uses in elseBlock are `null`, but probably not common.
1695 }
1696 }
1697
1698 collectIfUsers(HInstruction instruction,
1699 List<HInstruction> ifUsers,
1700 List<HInstruction> notIfUsers) {
1701 for (HInstruction user in instruction.usedBy) {
1702 if (user is HIf) {
1703 ifUsers.add(user);
1704 } else if (user is HNot) {
1705 collectIfUsers(user, notIfUsers, ifUsers);
1706 }
1707 }
1708 }
1709 }
1710
1711 /**
1712 * Optimization phase that tries to eliminate memory loads (for
1713 * example [HFieldGet]), when it knows the value stored in that memory
1714 * location.
1715 */
1716 class SsaLoadElimination extends HBaseVisitor implements OptimizationPhase {
1717 final Compiler compiler;
1718 final String name = "SsaLoadElimination";
1719 MemorySet memorySet;
1720 List<MemorySet> memories;
1721
1722 SsaLoadElimination(this.compiler);
1723
1724 void visitGraph(HGraph graph) {
1725 memories = new List<MemorySet>(graph.blocks.length);
1726 List<HBasicBlock> blocks = graph.blocks;
1727 for (int i = 0; i < blocks.length; i++) {
1728 HBasicBlock block = blocks[i];
1729 visitBasicBlock(block);
1730 if (block.successors.isNotEmpty && block.successors[0].isLoopHeader()) {
1731 // We've reached the ending block of a loop. Iterate over the
1732 // blocks of the loop again to take values that flow from that
1733 // ending block into account.
1734 for (int j = block.successors[0].id; j <= block.id; j++) {
1735 visitBasicBlock(blocks[j]);
1736 }
1737 }
1738 }
1739 }
1740
1741 void visitBasicBlock(HBasicBlock block) {
1742 if (block.predecessors.length == 0) {
1743 // Entry block.
1744 memorySet = new MemorySet(compiler);
1745 } else if (block.predecessors.length == 1
1746 && block.predecessors[0].successors.length == 1) {
1747 // No need to clone, there is no other successor for
1748 // `block.predecessors[0]`, and this block has only one
1749 // predecessor. Since we are not going to visit
1750 // `block.predecessors[0]` again, we can just re-use its
1751 // [memorySet].
1752 memorySet = memories[block.predecessors[0].id];
1753 } else if (block.predecessors.length == 1) {
1754 // Clone the memorySet of the predecessor, because it is also used
1755 // by other successors of it.
1756 memorySet = memories[block.predecessors[0].id].clone();
1757 } else {
1758 // Compute the intersection of all predecessors.
1759 memorySet = memories[block.predecessors[0].id];
1760 for (int i = 1; i < block.predecessors.length; i++) {
1761 memorySet = memorySet.intersectionFor(
1762 memories[block.predecessors[i].id], block, i);
1763 }
1764 }
1765
1766 memories[block.id] = memorySet;
1767 HInstruction instruction = block.first;
1768 while (instruction != null) {
1769 HInstruction next = instruction.next;
1770 instruction.accept(this);
1771 instruction = next;
1772 }
1773 }
1774
1775 void visitFieldGet(HFieldGet instruction) {
1776 if (instruction.isNullCheck) return;
1777 Element element = instruction.element;
1778 HInstruction receiver =
1779 instruction.getDartReceiver(compiler).nonCheck();
1780 HInstruction existing = memorySet.lookupFieldValue(element, receiver);
1781 if (existing != null) {
1782 instruction.block.rewriteWithBetterUser(instruction, existing);
1783 instruction.block.remove(instruction);
1784 } else {
1785 memorySet.registerFieldValue(element, receiver, instruction);
1786 }
1787 }
1788
1789 void visitFieldSet(HFieldSet instruction) {
1790 HInstruction receiver =
1791 instruction.getDartReceiver(compiler).nonCheck();
1792 memorySet.registerFieldValueUpdate(
1793 instruction.element, receiver, instruction.inputs.last);
1794 }
1795
1796 void visitForeignNew(HForeignNew instruction) {
1797 memorySet.registerAllocation(instruction);
1798 int argumentIndex = 0;
1799 instruction.element.forEachInstanceField((_, Element member) {
1800 memorySet.registerFieldValue(
1801 member, instruction, instruction.inputs[argumentIndex++]);
1802 }, includeSuperAndInjectedMembers: true);
1803 // In case this instruction has as input non-escaping objects, we
1804 // need to mark these objects as escaping.
1805 memorySet.killAffectedBy(instruction);
1806 }
1807
1808 void visitInstruction(HInstruction instruction) {
1809 memorySet.killAffectedBy(instruction);
1810 }
1811
1812 void visitLazyStatic(HLazyStatic instruction) {
1813 handleStaticLoad(instruction.element, instruction);
1814 }
1815
1816 void handleStaticLoad(Element element, HInstruction instruction) {
1817 HInstruction existing = memorySet.lookupFieldValue(element, null);
1818 if (existing != null) {
1819 instruction.block.rewriteWithBetterUser(instruction, existing);
1820 instruction.block.remove(instruction);
1821 } else {
1822 memorySet.registerFieldValue(element, null, instruction);
1823 }
1824 }
1825
1826 void visitStatic(HStatic instruction) {
1827 handleStaticLoad(instruction.element, instruction);
1828 }
1829
1830 void visitStaticStore(HStaticStore instruction) {
1831 memorySet.registerFieldValueUpdate(
1832 instruction.element, null, instruction.inputs.last);
1833 }
1834
1835 void visitLiteralList(HLiteralList instruction) {
1836 memorySet.registerAllocation(instruction);
1837 memorySet.killAffectedBy(instruction);
1838 }
1839
1840 void visitIndex(HIndex instruction) {
1841 HInstruction receiver = instruction.receiver.nonCheck();
1842 HInstruction existing =
1843 memorySet.lookupKeyedValue(receiver, instruction.index);
1844 if (existing != null) {
1845 instruction.block.rewriteWithBetterUser(instruction, existing);
1846 instruction.block.remove(instruction);
1847 } else {
1848 memorySet.registerKeyedValue(receiver, instruction.index, instruction);
1849 }
1850 }
1851
1852 void visitIndexAssign(HIndexAssign instruction) {
1853 HInstruction receiver = instruction.receiver.nonCheck();
1854 memorySet.registerKeyedValueUpdate(
1855 receiver, instruction.index, instruction.value);
1856 }
1857 }
1858
1859 /**
1860 * Holds values of memory places.
1861 */
1862 class MemorySet {
1863 final Compiler compiler;
1864
1865 /**
1866 * Maps a field to a map of receiver to value.
1867 */
1868 final Map<Element, Map<HInstruction, HInstruction>> fieldValues =
1869 <Element, Map<HInstruction, HInstruction>> {};
1870
1871 /**
1872 * Maps a receiver to a map of keys to value.
1873 */
1874 final Map<HInstruction, Map<HInstruction, HInstruction>> keyedValues =
1875 <HInstruction, Map<HInstruction, HInstruction>> {};
1876
1877 /**
1878 * Set of objects that we know don't escape the current function.
1879 */
1880 final Setlet<HInstruction> nonEscapingReceivers = new Setlet<HInstruction>();
1881
1882 MemorySet(this.compiler);
1883
1884 /**
1885 * Returns whether [first] and [second] always alias to the same object.
1886 */
1887 bool mustAlias(HInstruction first, HInstruction second) {
1888 return first == second;
1889 }
1890
1891 /**
1892 * Returns whether [first] and [second] may alias to the same object.
1893 */
1894 bool mayAlias(HInstruction first, HInstruction second) {
1895 if (mustAlias(first, second)) return true;
1896 if (isConcrete(first) && isConcrete(second)) return false;
1897 if (nonEscapingReceivers.contains(first)) return false;
1898 if (nonEscapingReceivers.contains(second)) return false;
1899 // Typed arrays of different types might have a shared buffer.
1900 if (couldBeTypedArray(first) && couldBeTypedArray(second)) return true;
1901 TypeMask intersection = first.instructionType.intersection(
1902 second.instructionType, compiler.world);
1903 if (intersection.isEmpty) return false;
1904 return true;
1905 }
1906
1907 bool isFinal(Element element) {
1908 return compiler.world.fieldNeverChanges(element);
1909 }
1910
1911 bool isConcrete(HInstruction instruction) {
1912 return instruction is HForeignNew
1913 || instruction is HConstant
1914 || instruction is HLiteralList;
1915 }
1916
1917 bool couldBeTypedArray(HInstruction receiver) {
1918 JavaScriptBackend backend = compiler.backend;
1919 return backend.couldBeTypedArray(receiver.instructionType);
1920 }
1921
1922 /**
1923 * Returns whether [receiver] escapes the current function.
1924 */
1925 bool escapes(HInstruction receiver) {
1926 return !nonEscapingReceivers.contains(receiver);
1927 }
1928
1929 void registerAllocation(HInstruction instruction) {
1930 nonEscapingReceivers.add(instruction);
1931 }
1932
1933 /**
1934 * Sets `receiver.element` to contain [value]. Kills all potential
1935 * places that may be affected by this update.
1936 */
1937 void registerFieldValueUpdate(Element element,
1938 HInstruction receiver,
1939 HInstruction value) {
1940 if (element.isNative) return; // TODO(14955): Remove this restriction?
1941 // [value] is being set in some place in memory, we remove it from
1942 // the non-escaping set.
1943 nonEscapingReceivers.remove(value);
1944 Map<HInstruction, HInstruction> map = fieldValues.putIfAbsent(
1945 element, () => <HInstruction, HInstruction> {});
1946 map.forEach((key, value) {
1947 if (mayAlias(receiver, key)) map[key] = null;
1948 });
1949 map[receiver] = value;
1950 }
1951
1952 /**
1953 * Registers that `receiver.element` is now [value].
1954 */
1955 void registerFieldValue(Element element,
1956 HInstruction receiver,
1957 HInstruction value) {
1958 if (element.isNative) return; // TODO(14955): Remove this restriction?
1959 Map<HInstruction, HInstruction> map = fieldValues.putIfAbsent(
1960 element, () => <HInstruction, HInstruction> {});
1961 map[receiver] = value;
1962 }
1963
1964 /**
1965 * Returns the value stored in `receiver.element`. Returns null if
1966 * we don't know.
1967 */
1968 HInstruction lookupFieldValue(Element element, HInstruction receiver) {
1969 Map<HInstruction, HInstruction> map = fieldValues[element];
1970 return (map == null) ? null : map[receiver];
1971 }
1972
1973 /**
1974 * Kill all places that may be affected by this [instruction]. Also
1975 * update the set of non-escaping objects in case [instruction] has
1976 * non-escaping objects in its inputs.
1977 */
1978 void killAffectedBy(HInstruction instruction) {
1979 // Even if [instruction] does not have side effects, it may use
1980 // non-escaping objects and store them in a new object, which
1981 // make these objects escaping.
1982 // TODO(ngeoffray): We need a new side effect flag to know whether
1983 // an instruction allocates an object.
1984 instruction.inputs.forEach((input) {
1985 nonEscapingReceivers.remove(input);
1986 });
1987
1988 if (instruction.sideEffects.changesInstanceProperty()
1989 || instruction.sideEffects.changesStaticProperty()) {
1990 fieldValues.forEach((element, map) {
1991 if (isFinal(element)) return;
1992 map.forEach((receiver, value) {
1993 if (escapes(receiver)) {
1994 map[receiver] = null;
1995 }
1996 });
1997 });
1998 }
1999
2000 if (instruction.sideEffects.changesIndex()) {
2001 keyedValues.forEach((receiver, map) {
2002 if (escapes(receiver)) {
2003 map.forEach((index, value) {
2004 map[index] = null;
2005 });
2006 }
2007 });
2008 }
2009 }
2010
2011 /**
2012 * Returns the value stored in `receiver[index]`. Returns null if
2013 * we don't know.
2014 */
2015 HInstruction lookupKeyedValue(HInstruction receiver, HInstruction index) {
2016 Map<HInstruction, HInstruction> map = keyedValues[receiver];
2017 return (map == null) ? null : map[index];
2018 }
2019
2020 /**
2021 * Registers that `receiver[index]` is now [value].
2022 */
2023 void registerKeyedValue(HInstruction receiver,
2024 HInstruction index,
2025 HInstruction value) {
2026 Map<HInstruction, HInstruction> map = keyedValues.putIfAbsent(
2027 receiver, () => <HInstruction, HInstruction> {});
2028 map[index] = value;
2029 }
2030
2031 /**
2032 * Sets `receiver[index]` to contain [value]. Kills all potential
2033 * places that may be affected by this update.
2034 */
2035 void registerKeyedValueUpdate(HInstruction receiver,
2036 HInstruction index,
2037 HInstruction value) {
2038 nonEscapingReceivers.remove(value);
2039 keyedValues.forEach((key, values) {
2040 if (mayAlias(receiver, key)) {
2041 // Typed arrays that are views of the same buffer may have different
2042 // offsets or element sizes, unless they are the same typed array.
2043 bool weakIndex = couldBeTypedArray(key) && !mustAlias(receiver, key);
2044 values.forEach((otherIndex, otherValue) {
2045 if (weakIndex || mayAlias(index, otherIndex)) {
2046 values[otherIndex] = null;
2047 }
2048 });
2049 }
2050 });
2051
2052 // Typed arrays may narrow incoming values.
2053 if (couldBeTypedArray(receiver)) return;
2054
2055 Map<HInstruction, HInstruction> map = keyedValues.putIfAbsent(
2056 receiver, () => <HInstruction, HInstruction> {});
2057 map[index] = value;
2058 }
2059
2060 /**
2061 * Returns null if either [first] or [second] is null. Otherwise
2062 * returns [first] if [first] and [second] are equal. Otherwise
2063 * creates or re-uses a phi in [block] that holds [first] and [second].
2064 */
2065 HInstruction findCommonInstruction(HInstruction first,
2066 HInstruction second,
2067 HBasicBlock block,
2068 int predecessorIndex) {
2069 if (first == null || second == null) return null;
2070 if (first == second) return first;
2071 TypeMask phiType = second.instructionType.union(
2072 first.instructionType, compiler.world);
2073 if (first is HPhi && first.block == block) {
2074 HPhi phi = first;
2075 phi.addInput(second);
2076 phi.instructionType = phiType;
2077 return phi;
2078 } else {
2079 HPhi phi = new HPhi.noInputs(null, phiType);
2080 block.addPhi(phi);
2081 // Previous predecessors had the same input. A phi must have
2082 // the same number of inputs as its block has predecessors.
2083 for (int i = 0; i < predecessorIndex; i++) {
2084 phi.addInput(first);
2085 }
2086 phi.addInput(second);
2087 return phi;
2088 }
2089 }
2090
2091 /**
2092 * Returns the intersection between [this] and [other].
2093 */
2094 MemorySet intersectionFor(MemorySet other,
2095 HBasicBlock block,
2096 int predecessorIndex) {
2097 MemorySet result = new MemorySet(compiler);
2098 if (other == null) return result;
2099
2100 fieldValues.forEach((element, values) {
2101 var otherValues = other.fieldValues[element];
2102 if (otherValues == null) return;
2103 values.forEach((receiver, value) {
2104 HInstruction instruction = findCommonInstruction(
2105 value, otherValues[receiver], block, predecessorIndex);
2106 if (instruction != null) {
2107 result.registerFieldValue(element, receiver, instruction);
2108 }
2109 });
2110 });
2111
2112 keyedValues.forEach((receiver, values) {
2113 var otherValues = other.keyedValues[receiver];
2114 if (otherValues == null) return;
2115 values.forEach((index, value) {
2116 HInstruction instruction = findCommonInstruction(
2117 value, otherValues[index], block, predecessorIndex);
2118 if (instruction != null) {
2119 result.registerKeyedValue(receiver, index, instruction);
2120 }
2121 });
2122 });
2123
2124 nonEscapingReceivers.forEach((receiver) {
2125 if (other.nonEscapingReceivers.contains(receiver)) {
2126 result.nonEscapingReceivers.add(receiver);
2127 }
2128 });
2129 return result;
2130 }
2131
2132 /**
2133 * Returns a copy of [this].
2134 */
2135 MemorySet clone() {
2136 MemorySet result = new MemorySet(compiler);
2137
2138 fieldValues.forEach((element, values) {
2139 result.fieldValues[element] =
2140 new Map<HInstruction, HInstruction>.from(values);
2141 });
2142
2143 keyedValues.forEach((receiver, values) {
2144 result.keyedValues[receiver] =
2145 new Map<HInstruction, HInstruction>.from(values);
2146 });
2147
2148 result.nonEscapingReceivers.addAll(nonEscapingReceivers);
2149 return result;
2150 }
2151 }
OLDNEW
« no previous file with comments | « sdk/lib/_internal/compiler/implementation/ssa/nodes.dart ('k') | sdk/lib/_internal/compiler/implementation/ssa/ssa.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698