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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: sdk/lib/_internal/compiler/implementation/ssa/optimize.dart
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/optimize.dart b/sdk/lib/_internal/compiler/implementation/ssa/optimize.dart
deleted file mode 100644
index 28f0632fd1ad6a96d65176f166615ed9a5305a26..0000000000000000000000000000000000000000
--- a/sdk/lib/_internal/compiler/implementation/ssa/optimize.dart
+++ /dev/null
@@ -1,2151 +0,0 @@
-// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-part of ssa;
-
-abstract class OptimizationPhase {
- String get name;
- void visitGraph(HGraph graph);
-}
-
-class SsaOptimizerTask extends CompilerTask {
- final JavaScriptBackend backend;
- SsaOptimizerTask(JavaScriptBackend backend)
- : this.backend = backend,
- super(backend.compiler);
- String get name => 'SSA optimizer';
- Compiler get compiler => backend.compiler;
- Map<HInstruction, Range> ranges = <HInstruction, Range>{};
-
- void runPhases(HGraph graph, List<OptimizationPhase> phases) {
- for (OptimizationPhase phase in phases) {
- runPhase(graph, phase);
- }
- }
-
- void runPhase(HGraph graph, OptimizationPhase phase) {
- phase.visitGraph(graph);
- compiler.tracer.traceGraph(phase.name, graph);
- assert(graph.isValid());
- }
-
- void optimize(CodegenWorkItem work, HGraph graph) {
- ConstantSystem constantSystem = compiler.backend.constantSystem;
- JavaScriptItemCompilationContext context = work.compilationContext;
- measure(() {
- SsaDeadCodeEliminator dce;
- List<OptimizationPhase> phases = <OptimizationPhase>[
- // Run trivial instruction simplification first to optimize
- // some patterns useful for type conversion.
- new SsaInstructionSimplifier(constantSystem, backend, work),
- new SsaTypeConversionInserter(compiler),
- new SsaRedundantPhiEliminator(),
- new SsaDeadPhiEliminator(),
- new SsaTypePropagator(compiler),
- // After type propagation, more instructions can be
- // simplified.
- new SsaInstructionSimplifier(constantSystem, backend, work),
- new SsaCheckInserter(backend, work, context.boundsChecked),
- new SsaInstructionSimplifier(constantSystem, backend, work),
- new SsaCheckInserter(backend, work, context.boundsChecked),
- new SsaTypePropagator(compiler),
- // Run a dead code eliminator before LICM because dead
- // interceptors are often in the way of LICM'able instructions.
- new SsaDeadCodeEliminator(compiler),
- new SsaGlobalValueNumberer(compiler),
- // After GVN, some instructions might need their type to be
- // updated because they now have different inputs.
- new SsaTypePropagator(compiler),
- new SsaCodeMotion(),
- new SsaLoadElimination(compiler),
- new SsaDeadPhiEliminator(),
- new SsaTypePropagator(compiler),
- new SsaValueRangeAnalyzer(compiler, constantSystem, work),
- // Previous optimizations may have generated new
- // opportunities for instruction simplification.
- new SsaInstructionSimplifier(constantSystem, backend, work),
- new SsaCheckInserter(backend, work, context.boundsChecked),
- new SsaSimplifyInterceptors(compiler, constantSystem, work),
- dce = new SsaDeadCodeEliminator(compiler),
- new SsaTypePropagator(compiler)];
- runPhases(graph, phases);
- if (dce.eliminatedSideEffects) {
- phases = <OptimizationPhase>[
- new SsaGlobalValueNumberer(compiler),
- new SsaCodeMotion(),
- new SsaValueRangeAnalyzer(compiler, constantSystem, work),
- new SsaInstructionSimplifier(constantSystem, backend, work),
- new SsaCheckInserter(backend, work, context.boundsChecked),
- new SsaSimplifyInterceptors(compiler, constantSystem, work),
- new SsaDeadCodeEliminator(compiler)];
- } else {
- phases = <OptimizationPhase>[
- // Run the simplifier to remove unneeded type checks inserted
- // by type propagation.
- new SsaInstructionSimplifier(constantSystem, backend, work)];
- }
- runPhases(graph, phases);
- });
- }
-}
-
-bool isFixedLength(mask, Compiler compiler) {
- ClassWorld classWorld = compiler.world;
- JavaScriptBackend backend = compiler.backend;
- if (mask.isContainer && mask.length != null) {
- // A container on which we have inferred the length.
- return true;
- } else if (mask.containsOnly(backend.jsFixedArrayClass)
- || mask.containsOnlyString(classWorld)
- || backend.isTypedArray(mask)) {
- return true;
- }
- return false;
-}
-
-/**
- * If both inputs to known operations are available execute the operation at
- * compile-time.
- */
-class SsaInstructionSimplifier extends HBaseVisitor
- implements OptimizationPhase {
-
- // We don't produce constant-folded strings longer than this unless they have
- // a single use. This protects against exponentially large constant folded
- // strings.
- static const MAX_SHARED_CONSTANT_FOLDED_STRING_LENGTH = 512;
-
- final String name = "SsaInstructionSimplifier";
- final JavaScriptBackend backend;
- final CodegenWorkItem work;
- final ConstantSystem constantSystem;
- HGraph graph;
- Compiler get compiler => backend.compiler;
-
- SsaInstructionSimplifier(this.constantSystem, this.backend, this.work);
-
- void visitGraph(HGraph visitee) {
- graph = visitee;
- visitDominatorTree(visitee);
- }
-
- visitBasicBlock(HBasicBlock block) {
- HInstruction instruction = block.first;
- while (instruction != null) {
- HInstruction next = instruction.next;
- HInstruction replacement = instruction.accept(this);
- if (replacement != instruction) {
- block.rewrite(instruction, replacement);
-
- // The intersection of double and int return conflicting, and
- // because of our number implementation for JavaScript, it
- // might be that an operation thought to return double, can be
- // simplified to an int. For example:
- // `2.5 * 10`.
- if (!(replacement.isNumberOrNull(compiler)
- && instruction.isNumberOrNull(compiler))) {
- // If we can replace [instruction] with [replacement], then
- // [replacement]'s type can be narrowed.
- TypeMask newType = replacement.instructionType.intersection(
- instruction.instructionType, compiler.world);
- replacement.instructionType = newType;
- }
-
- // If the replacement instruction does not know its
- // source element, use the source element of the
- // instruction.
- if (replacement.sourceElement == null) {
- replacement.sourceElement = instruction.sourceElement;
- }
- if (replacement.sourcePosition == null) {
- replacement.sourcePosition = instruction.sourcePosition;
- }
- if (!replacement.isInBasicBlock()) {
- // The constant folding can return an instruction that is already
- // part of the graph (like an input), so we only add the replacement
- // if necessary.
- block.addAfter(instruction, replacement);
- // Visit the replacement as the next instruction in case it
- // can also be constant folded away.
- next = replacement;
- }
- block.remove(instruction);
- }
- instruction = next;
- }
- }
-
- HInstruction visitInstruction(HInstruction node) {
- return node;
- }
-
- HInstruction visitBoolify(HBoolify node) {
- List<HInstruction> inputs = node.inputs;
- assert(inputs.length == 1);
- HInstruction input = inputs[0];
- if (input.isBoolean(compiler)) return input;
- // All values that cannot be 'true' are boolified to false.
- TypeMask mask = input.instructionType;
- if (!mask.contains(backend.jsBoolClass, compiler.world)) {
- return graph.addConstantBool(false, compiler);
- }
- return node;
- }
-
- HInstruction visitNot(HNot node) {
- List<HInstruction> inputs = node.inputs;
- assert(inputs.length == 1);
- HInstruction input = inputs[0];
- if (input is HConstant) {
- HConstant constant = input;
- bool isTrue = constant.constant.isTrue;
- return graph.addConstantBool(!isTrue, compiler);
- } else if (input is HNot) {
- return input.inputs[0];
- }
- return node;
- }
-
- HInstruction visitInvokeUnary(HInvokeUnary node) {
- HInstruction folded =
- foldUnary(node.operation(constantSystem), node.operand);
- return folded != null ? folded : node;
- }
-
- HInstruction foldUnary(UnaryOperation operation, HInstruction operand) {
- if (operand is HConstant) {
- HConstant receiver = operand;
- ConstantValue folded = operation.fold(receiver.constant);
- if (folded != null) return graph.addConstant(folded, compiler);
- }
- return null;
- }
-
- HInstruction tryOptimizeLengthInterceptedGetter(HInvokeDynamic node) {
- HInstruction actualReceiver = node.inputs[1];
- if (actualReceiver.isIndexablePrimitive(compiler)) {
- if (actualReceiver.isConstantString()) {
- HConstant constantInput = actualReceiver;
- StringConstantValue constant = constantInput.constant;
- return graph.addConstantInt(constant.length, compiler);
- } else if (actualReceiver.isConstantList()) {
- HConstant constantInput = actualReceiver;
- ListConstantValue constant = constantInput.constant;
- return graph.addConstantInt(constant.length, compiler);
- }
- Element element = backend.jsIndexableLength;
- bool isFixed = isFixedLength(actualReceiver.instructionType, compiler);
- HFieldGet result = new HFieldGet(
- element, actualReceiver, backend.positiveIntType,
- isAssignable: !isFixed);
- return result;
- } else if (actualReceiver.isConstantMap()) {
- HConstant constantInput = actualReceiver;
- MapConstantValue constant = constantInput.constant;
- return graph.addConstantInt(constant.length, compiler);
- }
- return null;
- }
-
- HInstruction handleInterceptedCall(HInvokeDynamic node) {
- // Try constant folding the instruction.
- Operation operation = node.specializer.operation(constantSystem);
- if (operation != null) {
- HInstruction instruction = node.inputs.length == 2
- ? foldUnary(operation, node.inputs[1])
- : foldBinary(operation, node.inputs[1], node.inputs[2]);
- if (instruction != null) return instruction;
- }
-
- // Try converting the instruction to a builtin instruction.
- HInstruction instruction =
- node.specializer.tryConvertToBuiltin(node, compiler);
- if (instruction != null) return instruction;
-
- Selector selector = node.selector;
- HInstruction input = node.inputs[1];
-
- World world = compiler.world;
- if (selector.isCall || selector.isOperator) {
- Element target;
- if (input.isExtendableArray(compiler)) {
- if (selector.applies(backend.jsArrayRemoveLast, world)) {
- target = backend.jsArrayRemoveLast;
- } else if (selector.applies(backend.jsArrayAdd, world)) {
- // The codegen special cases array calls, but does not
- // inline argument type checks.
- if (!compiler.enableTypeAssertions) {
- target = backend.jsArrayAdd;
- }
- }
- } else if (input.isStringOrNull(compiler)) {
- if (selector.applies(backend.jsStringSplit, world)) {
- HInstruction argument = node.inputs[2];
- if (argument.isString(compiler)) {
- target = backend.jsStringSplit;
- }
- } else if (selector.applies(backend.jsStringOperatorAdd, world)) {
- // `operator+` is turned into a JavaScript '+' so we need to
- // make sure the receiver and the argument are not null.
- // TODO(sra): Do this via [node.specializer].
- HInstruction argument = node.inputs[2];
- if (argument.isString(compiler)
- && !input.canBeNull()) {
- return new HStringConcat(input, argument, null,
- node.instructionType);
- }
- } else if (selector.applies(backend.jsStringToString, world)
- && !input.canBeNull()) {
- return input;
- }
- }
- if (target != null) {
- // TODO(ngeoffray): There is a strong dependency between codegen
- // and this optimization that the dynamic invoke does not need an
- // interceptor. We currently need to keep a
- // HInvokeDynamicMethod and not create a HForeign because
- // HForeign is too opaque for the SsaCheckInserter (that adds a
- // bounds check on removeLast). Once we start inlining, the
- // bounds check will become explicit, so we won't need this
- // optimization.
- HInvokeDynamicMethod result = new HInvokeDynamicMethod(
- node.selector, node.inputs.sublist(1), node.instructionType);
- result.element = target;
- return result;
- }
- } else if (selector.isGetter) {
- if (selector.asUntyped.applies(backend.jsIndexableLength, world)) {
- HInstruction optimized = tryOptimizeLengthInterceptedGetter(node);
- if (optimized != null) return optimized;
- }
- }
-
- return node;
- }
-
- HInstruction visitInvokeDynamicMethod(HInvokeDynamicMethod node) {
- if (node.isInterceptedCall) {
- HInstruction folded = handleInterceptedCall(node);
- if (folded != node) return folded;
- }
-
- TypeMask receiverType = node.getDartReceiver(compiler).instructionType;
- Selector selector =
- new TypedSelector(receiverType, node.selector, compiler.world);
- Element element = compiler.world.locateSingleElement(selector);
- // TODO(ngeoffray): Also fold if it's a getter or variable.
- if (element != null
- && element.isFunction
- // If we found out that the only target is a [:noSuchMethod:],
- // we just ignore it.
- && element.name == selector.name) {
- FunctionElement method = element;
-
- if (method.isNative) {
- HInstruction folded = tryInlineNativeMethod(node, method);
- if (folded != null) return folded;
- } else {
- // TODO(ngeoffray): If the method has optional parameters,
- // we should pass the default values.
- FunctionSignature parameters = method.functionSignature;
- if (parameters.optionalParameterCount == 0
- || parameters.parameterCount == node.selector.argumentCount) {
- node.element = element;
- }
- }
- }
- return node;
- }
-
- HInstruction tryInlineNativeMethod(HInvokeDynamicMethod node,
- FunctionElement method) {
- // Enable direct calls to a native method only if we don't run in checked
- // mode, where the Dart version may have type annotations on parameters and
- // return type that it should check.
- // Also check that the parameters are not functions: it's the callee that
- // will translate them to JS functions.
- //
- // TODO(ngeoffray): There are some cases where we could still inline in
- // checked mode if we know the arguments have the right type. And we could
- // do the closure conversion as well as the return type annotation check.
-
- if (!node.isInterceptedCall) return null;
-
- // TODO(sra): Check for legacy methods with bodies in the native strings.
- // foo() native 'return something';
- // They should not be used.
-
- FunctionSignature signature = method.functionSignature;
- if (signature.optionalParametersAreNamed) return null;
-
- // Return types on native methods don't need to be checked, since the
- // declaration has to be truthful.
-
- // The call site might omit optional arguments. The inlined code must
- // preserve the number of arguments, so check only the actual arguments.
-
- List<HInstruction> inputs = node.inputs.sublist(1);
- int inputPosition = 1; // Skip receiver.
- bool canInline = true;
- signature.forEachParameter((ParameterElement element) {
- if (inputPosition < inputs.length && canInline) {
- HInstruction input = inputs[inputPosition++];
- DartType type = element.type.unalias(compiler);
- if (type is FunctionType) {
- canInline = false;
- }
- if (compiler.enableTypeAssertions) {
- // TODO(sra): Check if [input] is guaranteed to pass the parameter
- // type check. Consider using a strengthened type check to avoid
- // passing `null` to primitive types since the native methods usually
- // have non-nullable primitive parameter types.
- canInline = false;
- }
- }
- });
-
- if (!canInline) return null;
-
- // Strengthen instruction type from annotations to help optimize
- // dependent instructions.
- native.NativeBehavior nativeBehavior =
- native.NativeBehavior.ofMethod(method, compiler);
- TypeMask returnType =
- TypeMaskFactory.fromNativeBehavior(nativeBehavior, compiler);
- HInvokeDynamicMethod result =
- new HInvokeDynamicMethod(node.selector, inputs, returnType);
- result.element = method;
- return result;
- }
-
- HInstruction visitBoundsCheck(HBoundsCheck node) {
- HInstruction index = node.index;
- if (index.isInteger(compiler)) return node;
- if (index.isConstant()) {
- HConstant constantInstruction = index;
- assert(!constantInstruction.constant.isInt);
- if (!constantSystem.isInt(constantInstruction.constant)) {
- // -0.0 is a double but will pass the runtime integer check.
- node.staticChecks = HBoundsCheck.ALWAYS_FALSE;
- }
- }
- return node;
- }
-
- HInstruction foldBinary(BinaryOperation operation,
- HInstruction left,
- HInstruction right) {
- if (left is HConstant && right is HConstant) {
- HConstant op1 = left;
- HConstant op2 = right;
- ConstantValue folded = operation.fold(op1.constant, op2.constant);
- if (folded != null) return graph.addConstant(folded, compiler);
- }
- return null;
- }
-
- HInstruction visitAdd(HAdd node) {
- HInstruction left = node.left;
- HInstruction right = node.right;
- // We can only perform this rewriting on Integer, as it is not
- // valid for -0.0.
- if (left.isInteger(compiler) && right.isInteger(compiler)) {
- if (left is HConstant && left.constant.isZero) return right;
- if (right is HConstant && right.constant.isZero) return left;
- }
- return super.visitAdd(node);
- }
-
- HInstruction visitMultiply(HMultiply node) {
- HInstruction left = node.left;
- HInstruction right = node.right;
- if (left.isNumber(compiler) && right.isNumber(compiler)) {
- if (left is HConstant && left.constant.isOne) return right;
- if (right is HConstant && right.constant.isOne) return left;
- }
- return super.visitMultiply(node);
- }
-
- HInstruction visitInvokeBinary(HInvokeBinary node) {
- HInstruction left = node.left;
- HInstruction right = node.right;
- BinaryOperation operation = node.operation(constantSystem);
- HConstant folded = foldBinary(operation, left, right);
- if (folded != null) return folded;
- return node;
- }
-
- bool allUsersAreBoolifies(HInstruction instruction) {
- List<HInstruction> users = instruction.usedBy;
- int length = users.length;
- for (int i = 0; i < length; i++) {
- if (users[i] is! HBoolify) return false;
- }
- return true;
- }
-
- HInstruction visitRelational(HRelational node) {
- if (allUsersAreBoolifies(node)) {
- // TODO(ngeoffray): Call a boolified selector.
- // This node stays the same, but the Boolify node will go away.
- }
- // Note that we still have to call [super] to make sure that we end up
- // in the remaining optimizations.
- return super.visitRelational(node);
- }
-
- HInstruction handleIdentityCheck(HRelational node) {
- HInstruction left = node.left;
- HInstruction right = node.right;
- TypeMask leftType = left.instructionType;
- TypeMask rightType = right.instructionType;
-
- // Intersection of int and double return conflicting, so
- // we don't optimize on numbers to preserve the runtime semantics.
- if (!(left.isNumberOrNull(compiler) && right.isNumberOrNull(compiler))) {
- TypeMask intersection = leftType.intersection(rightType, compiler.world);
- if (intersection.isEmpty && !intersection.isNullable) {
- return graph.addConstantBool(false, compiler);
- }
- }
-
- if (left.isNull() && right.isNull()) {
- return graph.addConstantBool(true, compiler);
- }
-
- if (left.isConstantBoolean() && right.isBoolean(compiler)) {
- HConstant constant = left;
- if (constant.constant.isTrue) {
- return right;
- } else {
- return new HNot(right, backend.boolType);
- }
- }
-
- if (right.isConstantBoolean() && left.isBoolean(compiler)) {
- HConstant constant = right;
- if (constant.constant.isTrue) {
- return left;
- } else {
- return new HNot(left, backend.boolType);
- }
- }
-
- return null;
- }
-
- HInstruction visitIdentity(HIdentity node) {
- HInstruction newInstruction = handleIdentityCheck(node);
- return newInstruction == null ? super.visitIdentity(node) : newInstruction;
- }
-
- void simplifyCondition(HBasicBlock block,
- HInstruction condition,
- bool value) {
- condition.dominatedUsers(block.first).forEach((user) {
- HInstruction newCondition = graph.addConstantBool(value, compiler);
- user.changeUse(condition, newCondition);
- });
- }
-
- HInstruction visitIf(HIf node) {
- HInstruction condition = node.condition;
- if (condition.isConstant()) return node;
- bool isNegated = condition is HNot;
-
- if (isNegated) {
- condition = condition.inputs[0];
- } else {
- // It is possible for LICM to move a negated version of the
- // condition out of the loop where it used. We still want to
- // simplify the nested use of the condition in that case, so
- // we look for all dominating negated conditions and replace
- // nested uses of them with true or false.
- Iterable<HInstruction> dominating = condition.usedBy.where((user) =>
- user is HNot && user.dominates(node));
- dominating.forEach((hoisted) {
- simplifyCondition(node.thenBlock, hoisted, false);
- simplifyCondition(node.elseBlock, hoisted, true);
- });
- }
- simplifyCondition(node.thenBlock, condition, !isNegated);
- simplifyCondition(node.elseBlock, condition, isNegated);
- return node;
- }
-
- HInstruction visitIs(HIs node) {
- DartType type = node.typeExpression;
- Element element = type.element;
-
- if (!node.isRawCheck) {
- return node;
- } else if (type.isTypedef) {
- return node;
- } else if (element == compiler.functionClass) {
- return node;
- }
-
- if (element == compiler.objectClass || type.treatAsDynamic) {
- return graph.addConstantBool(true, compiler);
- }
-
- ClassWorld classWorld = compiler.world;
- HInstruction expression = node.expression;
- if (expression.isInteger(compiler)) {
- if (identical(element, compiler.intClass)
- || identical(element, compiler.numClass)
- || Elements.isNumberOrStringSupertype(element, compiler)) {
- return graph.addConstantBool(true, compiler);
- } else if (identical(element, compiler.doubleClass)) {
- // We let the JS semantics decide for that check. Currently
- // the code we emit will always return true.
- return node;
- } else {
- return graph.addConstantBool(false, compiler);
- }
- } else if (expression.isDouble(compiler)) {
- if (identical(element, compiler.doubleClass)
- || identical(element, compiler.numClass)
- || Elements.isNumberOrStringSupertype(element, compiler)) {
- return graph.addConstantBool(true, compiler);
- } else if (identical(element, compiler.intClass)) {
- // We let the JS semantics decide for that check. Currently
- // the code we emit will return true for a double that can be
- // represented as a 31-bit integer and for -0.0.
- return node;
- } else {
- return graph.addConstantBool(false, compiler);
- }
- } else if (expression.isNumber(compiler)) {
- if (identical(element, compiler.numClass)) {
- return graph.addConstantBool(true, compiler);
- } else {
- // We cannot just return false, because the expression may be of
- // type int or double.
- }
- } else if (expression.canBePrimitiveNumber(compiler)
- && identical(element, compiler.intClass)) {
- // We let the JS semantics decide for that check.
- return node;
- // We need the [:hasTypeArguments:] check because we don't have
- // the notion of generics in the backend. For example, [:this:] in
- // a class [:A<T>:], is currently always considered to have the
- // raw type.
- } else if (!RuntimeTypes.hasTypeArguments(type)) {
- TypeMask expressionMask = expression.instructionType;
- assert(TypeMask.assertIsNormalized(expressionMask, classWorld));
- TypeMask typeMask = (element == compiler.nullClass)
- ? new TypeMask.subtype(element, classWorld)
- : new TypeMask.nonNullSubtype(element, classWorld);
- if (expressionMask.union(typeMask, classWorld) == typeMask) {
- return graph.addConstantBool(true, compiler);
- } else if (expressionMask.intersection(typeMask,
- compiler.world).isEmpty) {
- return graph.addConstantBool(false, compiler);
- }
- }
- return node;
- }
-
- HInstruction visitTypeConversion(HTypeConversion node) {
- HInstruction value = node.inputs[0];
- DartType type = node.typeExpression;
- if (type != null) {
- if (type.isMalformed) {
- // Malformed types are treated as dynamic statically, but should
- // throw a type error at runtime.
- return node;
- }
- if (!type.treatAsRaw || type.isTypeVariable) {
- return node;
- }
- if (type.isFunctionType) {
- // TODO(johnniwinther): Optimize function type conversions.
- return node;
- }
- }
- return removeIfCheckAlwaysSucceeds(node, node.checkedType);
- }
-
- HInstruction visitTypeKnown(HTypeKnown node) {
- return removeIfCheckAlwaysSucceeds(node, node.knownType);
- }
-
- HInstruction removeIfCheckAlwaysSucceeds(HCheck node, TypeMask checkedType) {
- ClassWorld classWorld = compiler.world;
- if (checkedType.containsAll(classWorld)) return node;
- HInstruction input = node.checkedInput;
- TypeMask inputType = input.instructionType;
- return inputType.isInMask(checkedType, classWorld) ? input : node;
- }
-
- VariableElement findConcreteFieldForDynamicAccess(HInstruction receiver,
- Selector selector) {
- TypeMask receiverType = receiver.instructionType;
- return compiler.world.locateSingleField(
- new TypedSelector(receiverType, selector, compiler.world));
- }
-
- HInstruction visitFieldGet(HFieldGet node) {
- if (node.isNullCheck) return node;
- var receiver = node.receiver;
- if (node.element == backend.jsIndexableLength) {
- JavaScriptItemCompilationContext context = work.compilationContext;
- if (context.allocatedFixedLists.contains(receiver)) {
- // TODO(ngeoffray): checking if the second input is an integer
- // should not be necessary but it currently makes it easier for
- // other optimizations to reason about a fixed length constructor
- // that we know takes an int.
- if (receiver.inputs[0].isInteger(compiler)) {
- return receiver.inputs[0];
- }
- } else if (receiver.isConstantList() || receiver.isConstantString()) {
- return graph.addConstantInt(receiver.constant.length, compiler);
- } else {
- var type = receiver.instructionType;
- if (type.isContainer && type.length != null) {
- HInstruction constant = graph.addConstantInt(type.length, compiler);
- if (type.isNullable) {
- // If the container can be null, we update all uses of the
- // length access to use the constant instead, but keep the
- // length access in the graph, to ensure we still have a
- // null check.
- node.block.rewrite(node, constant);
- return node;
- } else {
- return constant;
- }
- }
- }
- }
-
- // HFieldGet of a constructed constant can be replaced with the constant's
- // field.
- if (receiver is HConstant) {
- ConstantValue constant = receiver.constant;
- if (constant.isConstructedObject) {
- ConstructedConstantValue constructedConstant = constant;
- Map<Element, ConstantValue> fields = constructedConstant.fieldElements;
- ConstantValue value = fields[node.element];
- if (value != null) {
- return graph.addConstant(value, compiler);
- }
- }
- }
-
- return node;
- }
-
- HInstruction visitIndex(HIndex node) {
- if (node.receiver.isConstantList() && node.index.isConstantInteger()) {
- var instruction = node.receiver;
- List<ConstantValue> entries = instruction.constant.entries;
- instruction = node.index;
- int index = instruction.constant.primitiveValue;
- if (index >= 0 && index < entries.length) {
- return graph.addConstant(entries[index], compiler);
- }
- }
- return node;
- }
-
- HInstruction visitInvokeDynamicGetter(HInvokeDynamicGetter node) {
- if (node.isInterceptedCall) {
- HInstruction folded = handleInterceptedCall(node);
- if (folded != node) return folded;
- }
- HInstruction receiver = node.getDartReceiver(compiler);
- Element field = findConcreteFieldForDynamicAccess(receiver, node.selector);
- if (field == null) return node;
- return directFieldGet(receiver, field);
- }
-
- HInstruction directFieldGet(HInstruction receiver, Element field) {
- bool isAssignable = !compiler.world.fieldNeverChanges(field);
-
- TypeMask type;
- if (field.enclosingClass.isNative) {
- type = TypeMaskFactory.fromNativeBehavior(
- native.NativeBehavior.ofFieldLoad(field, compiler),
- compiler);
- } else {
- type = TypeMaskFactory.inferredTypeForElement(field, compiler);
- }
-
- return new HFieldGet(
- field, receiver, type, isAssignable: isAssignable);
- }
-
- HInstruction visitInvokeDynamicSetter(HInvokeDynamicSetter node) {
- if (node.isInterceptedCall) {
- HInstruction folded = handleInterceptedCall(node);
- if (folded != node) return folded;
- }
-
- HInstruction receiver = node.getDartReceiver(compiler);
- VariableElement field =
- findConcreteFieldForDynamicAccess(receiver, node.selector);
- if (field == null || !field.isAssignable) return node;
- // Use [:node.inputs.last:] in case the call follows the
- // interceptor calling convention, but is not a call on an
- // interceptor.
- HInstruction value = node.inputs.last;
- if (compiler.enableTypeAssertions) {
- DartType type = field.type;
- if (!type.treatAsRaw || type.isTypeVariable) {
- // We cannot generate the correct type representation here, so don't
- // inline this access.
- return node;
- }
- HInstruction other = value.convertType(
- compiler,
- type,
- HTypeConversion.CHECKED_MODE_CHECK);
- if (other != value) {
- node.block.addBefore(node, other);
- value = other;
- }
- }
- return new HFieldSet(field, receiver, value);
- }
-
- HInstruction visitStringConcat(HStringConcat node) {
- // Simplify string concat:
- //
- // "" + R -> R
- // L + "" -> L
- // "L" + "R" -> "LR"
- // (prefix + "L") + "R" -> prefix + "LR"
- //
- StringConstantValue getString(HInstruction instruction) {
- if (!instruction.isConstantString()) return null;
- HConstant constant = instruction;
- return constant.constant;
- }
-
- StringConstantValue leftString = getString(node.left);
- if (leftString != null && leftString.primitiveValue.length == 0) {
- return node.right;
- }
-
- StringConstantValue rightString = getString(node.right);
- if (rightString == null) return node;
- if (rightString.primitiveValue.length == 0) return node.left;
-
- HInstruction prefix = null;
- if (leftString == null) {
- if (node.left is! HStringConcat) return node;
- HStringConcat leftConcat = node.left;
- // Don't undo CSE.
- if (leftConcat.usedBy.length != 1) return node;
- prefix = leftConcat.left;
- leftString = getString(leftConcat.right);
- if (leftString == null) return node;
- }
-
- if (leftString.primitiveValue.length + rightString.primitiveValue.length >
- MAX_SHARED_CONSTANT_FOLDED_STRING_LENGTH) {
- if (node.usedBy.length > 1) return node;
- }
-
- HInstruction folded = graph.addConstant(
- constantSystem.createString(new ast.DartString.concat(
- leftString.primitiveValue, rightString.primitiveValue)),
- compiler);
- if (prefix == null) return folded;
- return new HStringConcat(prefix, folded, node.node, backend.stringType);
- }
-
- HInstruction visitStringify(HStringify node) {
- HInstruction input = node.inputs[0];
- if (input.isString(compiler)) return input;
- if (input.isConstant()) {
- HConstant constant = input;
- if (!constant.constant.isPrimitive) return node;
- if (constant.constant.isInt) {
- // Only constant-fold int.toString() when Dart and JS results the same.
- // TODO(18103): We should be able to remove this work-around when issue
- // 18103 is resolved by providing the correct string.
- IntConstantValue intConstant = constant.constant;
- // Very conservative range.
- if (!intConstant.isUInt32()) return node;
- }
- PrimitiveConstantValue primitive = constant.constant;
- return graph.addConstant(constantSystem.createString(
- primitive.toDartString()), compiler);
- }
- return node;
- }
-
- HInstruction visitOneShotInterceptor(HOneShotInterceptor node) {
- return handleInterceptedCall(node);
- }
-}
-
-class SsaCheckInserter extends HBaseVisitor implements OptimizationPhase {
- final Set<HInstruction> boundsChecked;
- final CodegenWorkItem work;
- final JavaScriptBackend backend;
- final String name = "SsaCheckInserter";
- HGraph graph;
-
- SsaCheckInserter(this.backend,
- this.work,
- this.boundsChecked);
-
- void visitGraph(HGraph graph) {
- this.graph = graph;
- visitDominatorTree(graph);
- }
-
- void visitBasicBlock(HBasicBlock block) {
- HInstruction instruction = block.first;
- while (instruction != null) {
- HInstruction next = instruction.next;
- instruction = instruction.accept(this);
- instruction = next;
- }
- }
-
- HBoundsCheck insertBoundsCheck(HInstruction indexNode,
- HInstruction array,
- HInstruction indexArgument) {
- Compiler compiler = backend.compiler;
- HFieldGet length = new HFieldGet(
- backend.jsIndexableLength, array, backend.positiveIntType,
- isAssignable: !isFixedLength(array.instructionType, compiler));
- indexNode.block.addBefore(indexNode, length);
-
- TypeMask type = indexArgument.isPositiveInteger(compiler)
- ? indexArgument.instructionType
- : backend.positiveIntType;
- HBoundsCheck check = new HBoundsCheck(
- indexArgument, length, array, type);
- indexNode.block.addBefore(indexNode, check);
- // If the index input to the bounds check was not known to be an integer
- // then we replace its uses with the bounds check, which is known to be an
- // integer. However, if the input was already an integer we don't do this
- // because putting in a check instruction might obscure the real nature of
- // the index eg. if it is a constant. The range information from the
- // BoundsCheck instruction is attached to the input directly by
- // visitBoundsCheck in the SsaValueRangeAnalyzer.
- if (!indexArgument.isInteger(compiler)) {
- indexArgument.replaceAllUsersDominatedBy(indexNode, check);
- }
- boundsChecked.add(indexNode);
- return check;
- }
-
- void visitIndex(HIndex node) {
- if (boundsChecked.contains(node)) return;
- HInstruction index = node.index;
- index = insertBoundsCheck(node, node.receiver, index);
- }
-
- void visitIndexAssign(HIndexAssign node) {
- if (boundsChecked.contains(node)) return;
- HInstruction index = node.index;
- index = insertBoundsCheck(node, node.receiver, index);
- }
-
- void visitInvokeDynamicMethod(HInvokeDynamicMethod node) {
- Element element = node.element;
- if (node.isInterceptedCall) return;
- if (element != backend.jsArrayRemoveLast) return;
- if (boundsChecked.contains(node)) return;
- insertBoundsCheck(
- node, node.receiver, graph.addConstantInt(0, backend.compiler));
- }
-}
-
-class SsaDeadCodeEliminator extends HGraphVisitor implements OptimizationPhase {
- final String name = "SsaDeadCodeEliminator";
-
- final Compiler compiler;
- SsaLiveBlockAnalyzer analyzer;
- bool eliminatedSideEffects = false;
- SsaDeadCodeEliminator(this.compiler);
-
- HInstruction zapInstructionCache;
- HInstruction get zapInstruction {
- if (zapInstructionCache == null) {
- // A constant with no type does not pollute types at phi nodes.
- ConstantValue constant =
- new DummyConstantValue(const TypeMask.nonNullEmpty());
- zapInstructionCache = analyzer.graph.addConstant(constant, compiler);
- }
- return zapInstructionCache;
- }
-
- /// Returns whether the next throwing instruction that may have side
- /// effects after [instruction], throws [NoSuchMethodError] on the
- /// same receiver of [instruction].
- bool hasFollowingThrowingNSM(HInstruction instruction) {
- HInstruction receiver = instruction.getDartReceiver(compiler);
- HInstruction current = instruction.next;
- do {
- if ((current.getDartReceiver(compiler) == receiver)
- && current.canThrow()) {
- return true;
- }
- if (current.canThrow() || current.sideEffects.hasSideEffects()) {
- return false;
- }
- if (current.next == null && current is HGoto) {
- // We do not merge blocks in our SSA graph, so if this block
- // just jumps to a single predecessor, visit this predecessor.
- assert(current.block.successors.length == 1);
- current = current.block.successors[0].first;
- } else {
- current = current.next;
- }
- } while (current != null);
- return false;
- }
-
- bool isDeadCode(HInstruction instruction) {
- if (!instruction.usedBy.isEmpty) return false;
- if (instruction.sideEffects.hasSideEffects()) return false;
- if (instruction.canThrow()
- && instruction.onlyThrowsNSM()
- && hasFollowingThrowingNSM(instruction)) {
- return true;
- }
- return !instruction.canThrow()
- && instruction is !HParameterValue
- && instruction is !HLocalSet;
- }
-
- void visitGraph(HGraph graph) {
- analyzer = new SsaLiveBlockAnalyzer(graph, compiler);
- analyzer.analyze();
- visitPostDominatorTree(graph);
- cleanPhis(graph);
- }
-
- void visitBasicBlock(HBasicBlock block) {
- bool isDeadBlock = analyzer.isDeadBlock(block);
- block.isLive = !isDeadBlock;
- // Start from the last non-control flow instruction in the block.
- HInstruction instruction = block.last.previous;
- while (instruction != null) {
- var previous = instruction.previous;
- if (isDeadBlock) {
- eliminatedSideEffects = eliminatedSideEffects ||
- instruction.sideEffects.hasSideEffects();
- removeUsers(instruction);
- block.remove(instruction);
- } else if (isDeadCode(instruction)) {
- block.remove(instruction);
- }
- instruction = previous;
- }
- }
-
- void cleanPhis(HGraph graph) {
- L: for (HBasicBlock block in graph.blocks) {
- List<HBasicBlock> predecessors = block.predecessors;
- // Zap all inputs to phis that correspond to dead blocks.
- block.forEachPhi((HPhi phi) {
- for (int i = 0; i < phi.inputs.length; ++i) {
- if (!predecessors[i].isLive && phi.inputs[i] != zapInstruction) {
- replaceInput(i, phi, zapInstruction);
- }
- }
- });
- if (predecessors.length < 2) continue L;
- // Find the index of the single live predecessor if it exists.
- int indexOfLive = -1;
- for (int i = 0; i < predecessors.length; i++) {
- if (predecessors[i].isLive) {
- if (indexOfLive >= 0) continue L;
- indexOfLive = i;
- }
- }
- // Run through the phis of the block and replace them with their input
- // that comes from the only live predecessor if that dominates the phi.
- block.forEachPhi((HPhi phi) {
- HInstruction replacement = (indexOfLive >= 0)
- ? phi.inputs[indexOfLive] : zapInstruction;
- if (replacement.dominates(phi)) {
- block.rewrite(phi, replacement);
- block.removePhi(phi);
- }
- });
- }
- }
-
- void replaceInput(int i, HInstruction from, HInstruction by) {
- from.inputs[i].usedBy.remove(from);
- from.inputs[i] = by;
- by.usedBy.add(from);
- }
-
- void removeUsers(HInstruction instruction) {
- instruction.usedBy.forEach((user) {
- removeInput(user, instruction);
- });
- instruction.usedBy.clear();
- }
-
- void removeInput(HInstruction user, HInstruction input) {
- List<HInstruction> inputs = user.inputs;
- for (int i = 0, length = inputs.length; i < length; i++) {
- if (input == inputs[i]) {
- user.inputs[i] = zapInstruction;
- zapInstruction.usedBy.add(user);
- }
- }
- }
-}
-
-class SsaLiveBlockAnalyzer extends HBaseVisitor {
- final HGraph graph;
- final Compiler compiler;
- final Set<HBasicBlock> live = new Set<HBasicBlock>();
- final List<HBasicBlock> worklist = <HBasicBlock>[];
-
- SsaLiveBlockAnalyzer(this.graph, this.compiler);
-
- JavaScriptBackend get backend => compiler.backend;
- Map<HInstruction, Range> get ranges => backend.optimizer.ranges;
-
- bool isDeadBlock(HBasicBlock block) => !live.contains(block);
-
- void analyze() {
- markBlockLive(graph.entry);
- while (!worklist.isEmpty) {
- HBasicBlock live = worklist.removeLast();
- live.last.accept(this);
- }
- }
-
- void markBlockLive(HBasicBlock block) {
- if (!live.contains(block)) {
- worklist.add(block);
- live.add(block);
- }
- }
-
- void visitControlFlow(HControlFlow instruction) {
- instruction.block.successors.forEach(markBlockLive);
- }
-
- void visitIf(HIf instruction) {
- HInstruction condition = instruction.condition;
- if (condition.isConstant()) {
- if (condition.isConstantTrue()) {
- markBlockLive(instruction.thenBlock);
- } else {
- markBlockLive(instruction.elseBlock);
- }
- } else {
- visitControlFlow(instruction);
- }
- }
-
- void visitSwitch(HSwitch node) {
- if (node.expression.isInteger(compiler)) {
- Range switchRange = ranges[node.expression];
- if (switchRange != null &&
- switchRange.lower is IntValue &&
- switchRange.upper is IntValue) {
- IntValue lowerValue = switchRange.lower;
- IntValue upperValue = switchRange.upper;
- int lower = lowerValue.value;
- int upper = upperValue.value;
- Set<int> liveLabels = new Set<int>();
- for (int pos = 1; pos < node.inputs.length; pos++) {
- HConstant input = node.inputs[pos];
- if (!input.isConstantInteger()) continue;
- IntConstantValue constant = input.constant;
- int label = constant.primitiveValue;
- if (!liveLabels.contains(label) &&
- label <= upper &&
- label >= lower) {
- markBlockLive(node.block.successors[pos - 1]);
- liveLabels.add(label);
- }
- }
- if (liveLabels.length != upper - lower + 1) {
- markBlockLive(node.defaultTarget);
- }
- return;
- }
- }
- visitControlFlow(node);
- }
-}
-
-class SsaDeadPhiEliminator implements OptimizationPhase {
- final String name = "SsaDeadPhiEliminator";
-
- void visitGraph(HGraph graph) {
- final List<HPhi> worklist = <HPhi>[];
- // A set to keep track of the live phis that we found.
- final Set<HPhi> livePhis = new Set<HPhi>();
-
- // Add to the worklist all live phis: phis referenced by non-phi
- // instructions.
- for (final block in graph.blocks) {
- block.forEachPhi((HPhi phi) {
- for (final user in phi.usedBy) {
- if (user is !HPhi) {
- worklist.add(phi);
- livePhis.add(phi);
- break;
- }
- }
- });
- }
-
- // Process the worklist by propagating liveness to phi inputs.
- while (!worklist.isEmpty) {
- HPhi phi = worklist.removeLast();
- for (final input in phi.inputs) {
- if (input is HPhi && !livePhis.contains(input)) {
- worklist.add(input);
- livePhis.add(input);
- }
- }
- }
-
- // Remove phis that are not live.
- // Traverse in reverse order to remove phis with no uses before the
- // phis that they might use.
- // NOTICE: Doesn't handle circular references, but we don't currently
- // create any.
- List<HBasicBlock> blocks = graph.blocks;
- for (int i = blocks.length - 1; i >= 0; i--) {
- HBasicBlock block = blocks[i];
- HPhi current = block.phis.first;
- HPhi next = null;
- while (current != null) {
- next = current.next;
- if (!livePhis.contains(current)
- // TODO(ahe): Not sure the following is correct.
- && current.usedBy.isEmpty) {
- block.removePhi(current);
- }
- current = next;
- }
- }
- }
-}
-
-class SsaRedundantPhiEliminator implements OptimizationPhase {
- final String name = "SsaRedundantPhiEliminator";
-
- void visitGraph(HGraph graph) {
- final List<HPhi> worklist = <HPhi>[];
-
- // Add all phis in the worklist.
- for (final block in graph.blocks) {
- block.forEachPhi((HPhi phi) => worklist.add(phi));
- }
-
- while (!worklist.isEmpty) {
- HPhi phi = worklist.removeLast();
-
- // If the phi has already been processed, continue.
- if (!phi.isInBasicBlock()) continue;
-
- // Find if the inputs of the phi are the same instruction.
- // The builder ensures that phi.inputs[0] cannot be the phi
- // itself.
- assert(!identical(phi.inputs[0], phi));
- HInstruction candidate = phi.inputs[0];
- for (int i = 1; i < phi.inputs.length; i++) {
- HInstruction input = phi.inputs[i];
- // If the input is the phi, the phi is still candidate for
- // elimination.
- if (!identical(input, candidate) && !identical(input, phi)) {
- candidate = null;
- break;
- }
- }
-
- // If the inputs are not the same, continue.
- if (candidate == null) continue;
-
- // Because we're updating the users of this phi, we may have new
- // phis candidate for elimination. Add phis that used this phi
- // to the worklist.
- for (final user in phi.usedBy) {
- if (user is HPhi) worklist.add(user);
- }
- phi.block.rewrite(phi, candidate);
- phi.block.removePhi(phi);
- }
- }
-}
-
-class GvnWorkItem {
- final HBasicBlock block;
- final ValueSet valueSet;
- GvnWorkItem(this.block, this.valueSet);
-}
-
-class SsaGlobalValueNumberer implements OptimizationPhase {
- final String name = "SsaGlobalValueNumberer";
- final Compiler compiler;
- final Set<int> visited;
-
- List<int> blockChangesFlags;
- List<int> loopChangesFlags;
-
- SsaGlobalValueNumberer(this.compiler) : visited = new Set<int>();
-
- void visitGraph(HGraph graph) {
- computeChangesFlags(graph);
- moveLoopInvariantCode(graph);
- List<GvnWorkItem> workQueue =
- <GvnWorkItem>[new GvnWorkItem(graph.entry, new ValueSet())];
- do {
- GvnWorkItem item = workQueue.removeLast();
- visitBasicBlock(item.block, item.valueSet, workQueue);
- } while (!workQueue.isEmpty);
- }
-
- void moveLoopInvariantCode(HGraph graph) {
- for (int i = graph.blocks.length - 1; i >= 0; i--) {
- HBasicBlock block = graph.blocks[i];
- if (block.isLoopHeader()) {
- int changesFlags = loopChangesFlags[block.id];
- HLoopInformation info = block.loopInformation;
- // Iterate over all blocks of this loop. Note that blocks in
- // inner loops are not visited here, but we know they
- // were visited before because we are iterating in post-order.
- // So instructions that are GVN'ed in an inner loop are in their
- // loop entry, and [info.blocks] contains this loop entry.
- moveLoopInvariantCodeFromBlock(block, block, changesFlags);
- for (HBasicBlock other in info.blocks) {
- moveLoopInvariantCodeFromBlock(other, block, changesFlags);
- }
- }
- }
- }
-
- void moveLoopInvariantCodeFromBlock(HBasicBlock block,
- HBasicBlock loopHeader,
- int changesFlags) {
- assert(block.parentLoopHeader == loopHeader || block == loopHeader);
- HBasicBlock preheader = loopHeader.predecessors[0];
- int dependsFlags = SideEffects.computeDependsOnFlags(changesFlags);
- HInstruction instruction = block.first;
- bool isLoopAlwaysTaken() {
- HInstruction instruction = loopHeader.last;
- assert(instruction is HGoto || instruction is HLoopBranch);
- return instruction is HGoto
- || instruction.inputs[0].isConstantTrue();
- }
- bool firstInstructionInLoop = block == loopHeader
- // Compensate for lack of code motion.
- || (blockChangesFlags[loopHeader.id] == 0
- && isLoopAlwaysTaken()
- && loopHeader.successors[0] == block);
- while (instruction != null) {
- HInstruction next = instruction.next;
- if (instruction.useGvn() && instruction.isMovable
- && (!instruction.canThrow() || firstInstructionInLoop)
- && !instruction.sideEffects.dependsOn(dependsFlags)) {
- bool loopInvariantInputs = true;
- List<HInstruction> inputs = instruction.inputs;
- for (int i = 0, length = inputs.length; i < length; i++) {
- if (isInputDefinedAfterDominator(inputs[i], preheader)) {
- loopInvariantInputs = false;
- break;
- }
- }
-
- // If the inputs are loop invariant, we can move the
- // instruction from the current block to the pre-header block.
- if (loopInvariantInputs) {
- block.detach(instruction);
- preheader.moveAtExit(instruction);
- } else {
- firstInstructionInLoop = false;
- }
- }
- int oldChangesFlags = changesFlags;
- changesFlags |= instruction.sideEffects.getChangesFlags();
- if (oldChangesFlags != changesFlags) {
- dependsFlags = SideEffects.computeDependsOnFlags(changesFlags);
- }
- instruction = next;
- }
- }
-
- bool isInputDefinedAfterDominator(HInstruction input,
- HBasicBlock dominator) {
- return input.block.id > dominator.id;
- }
-
- void visitBasicBlock(
- HBasicBlock block, ValueSet values, List<GvnWorkItem> workQueue) {
- HInstruction instruction = block.first;
- if (block.isLoopHeader()) {
- int flags = loopChangesFlags[block.id];
- values.kill(flags);
- }
- while (instruction != null) {
- HInstruction next = instruction.next;
- int flags = instruction.sideEffects.getChangesFlags();
- assert(flags == 0 || !instruction.useGvn());
- values.kill(flags);
- if (instruction.useGvn()) {
- HInstruction other = values.lookup(instruction);
- if (other != null) {
- assert(other.gvnEquals(instruction) && instruction.gvnEquals(other));
- block.rewriteWithBetterUser(instruction, other);
- block.remove(instruction);
- } else {
- values.add(instruction);
- }
- }
- instruction = next;
- }
-
- List<HBasicBlock> dominatedBlocks = block.dominatedBlocks;
- for (int i = 0, length = dominatedBlocks.length; i < length; i++) {
- HBasicBlock dominated = dominatedBlocks[i];
- // No need to copy the value set for the last child.
- ValueSet successorValues = (i == length - 1) ? values : values.copy();
- // If we have no values in our set, we do not have to kill
- // anything. Also, if the range of block ids from the current
- // block to the dominated block is empty, there is no blocks on
- // any path from the current block to the dominated block so we
- // don't have to do anything either.
- assert(block.id < dominated.id);
- if (!successorValues.isEmpty && block.id + 1 < dominated.id) {
- visited.clear();
- List<HBasicBlock> workQueue = <HBasicBlock>[dominated];
- int changesFlags = 0;
- do {
- HBasicBlock current = workQueue.removeLast();
- changesFlags |=
- getChangesFlagsForDominatedBlock(block, current, workQueue);
- } while (!workQueue.isEmpty);
- successorValues.kill(changesFlags);
- }
- workQueue.add(new GvnWorkItem(dominated, successorValues));
- }
- }
-
- void computeChangesFlags(HGraph graph) {
- // Create the changes flags lists. Make sure to initialize the
- // loop changes flags list to zero so we can use bitwise or when
- // propagating loop changes upwards.
- final int length = graph.blocks.length;
- blockChangesFlags = new List<int>(length);
- loopChangesFlags = new List<int>(length);
- for (int i = 0; i < length; i++) loopChangesFlags[i] = 0;
-
- // Run through all the basic blocks in the graph and fill in the
- // changes flags lists.
- for (int i = length - 1; i >= 0; i--) {
- final HBasicBlock block = graph.blocks[i];
- final int id = block.id;
-
- // Compute block changes flags for the block.
- int changesFlags = 0;
- HInstruction instruction = block.first;
- while (instruction != null) {
- changesFlags |= instruction.sideEffects.getChangesFlags();
- instruction = instruction.next;
- }
- assert(blockChangesFlags[id] == null);
- blockChangesFlags[id] = changesFlags;
-
- // Loop headers are part of their loop, so update the loop
- // changes flags accordingly.
- if (block.isLoopHeader()) {
- loopChangesFlags[id] |= changesFlags;
- }
-
- // Propagate loop changes flags upwards.
- HBasicBlock parentLoopHeader = block.parentLoopHeader;
- if (parentLoopHeader != null) {
- loopChangesFlags[parentLoopHeader.id] |= (block.isLoopHeader())
- ? loopChangesFlags[id]
- : changesFlags;
- }
- }
- }
-
- int getChangesFlagsForDominatedBlock(HBasicBlock dominator,
- HBasicBlock dominated,
- List<HBasicBlock> workQueue) {
- int changesFlags = 0;
- List<HBasicBlock> predecessors = dominated.predecessors;
- for (int i = 0, length = predecessors.length; i < length; i++) {
- HBasicBlock block = predecessors[i];
- int id = block.id;
- // If the current predecessor block is on the path from the
- // dominator to the dominated, it must have an id that is in the
- // range from the dominator to the dominated.
- if (dominator.id < id && id < dominated.id && !visited.contains(id)) {
- visited.add(id);
- changesFlags |= blockChangesFlags[id];
- // Loop bodies might not be on the path from dominator to dominated,
- // but they can invalidate values.
- changesFlags |= loopChangesFlags[id];
- workQueue.add(block);
- }
- }
- return changesFlags;
- }
-}
-
-// This phase merges equivalent instructions on different paths into
-// one instruction in a dominator block. It runs through the graph
-// post dominator order and computes a ValueSet for each block of
-// instructions that can be moved to a dominator block. These
-// instructions are the ones that:
-// 1) can be used for GVN, and
-// 2) do not use definitions of their own block.
-//
-// A basic block looks at its sucessors and finds the intersection of
-// these computed ValueSet. It moves all instructions of the
-// intersection into its own list of instructions.
-class SsaCodeMotion extends HBaseVisitor implements OptimizationPhase {
- final String name = "SsaCodeMotion";
-
- List<ValueSet> values;
-
- void visitGraph(HGraph graph) {
- values = new List<ValueSet>(graph.blocks.length);
- for (int i = 0; i < graph.blocks.length; i++) {
- values[graph.blocks[i].id] = new ValueSet();
- }
- visitPostDominatorTree(graph);
- }
-
- void visitBasicBlock(HBasicBlock block) {
- List<HBasicBlock> successors = block.successors;
-
- // Phase 1: get the ValueSet of all successors (if there are more than one),
- // compute the intersection and move the instructions of the intersection
- // into this block.
- if (successors.length > 1) {
- ValueSet instructions = values[successors[0].id];
- for (int i = 1; i < successors.length; i++) {
- ValueSet other = values[successors[i].id];
- instructions = instructions.intersection(other);
- }
-
- if (!instructions.isEmpty) {
- List<HInstruction> list = instructions.toList();
- for (HInstruction instruction in list) {
- // Move the instruction to the current block.
- instruction.block.detach(instruction);
- block.moveAtExit(instruction);
- // Go through all successors and rewrite their instruction
- // to the shared one.
- for (final successor in successors) {
- HInstruction toRewrite = values[successor.id].lookup(instruction);
- if (toRewrite != instruction) {
- successor.rewriteWithBetterUser(toRewrite, instruction);
- successor.remove(toRewrite);
- }
- }
- }
- }
- }
-
- // Don't try to merge instructions to a dominator if we have
- // multiple predecessors.
- if (block.predecessors.length != 1) return;
-
- // Phase 2: Go through all instructions of this block and find
- // which instructions can be moved to a dominator block.
- ValueSet set_ = values[block.id];
- HInstruction instruction = block.first;
- int flags = 0;
- while (instruction != null) {
- int dependsFlags = SideEffects.computeDependsOnFlags(flags);
- flags |= instruction.sideEffects.getChangesFlags();
-
- HInstruction current = instruction;
- instruction = instruction.next;
- if (!current.useGvn() || !current.isMovable) continue;
- // TODO(sra): We could move throwing instructions provided we keep the
- // exceptions in the same order. This requires they are in the same order
- // in all successors, which is not tracked by the ValueSet.
- if (current.canThrow()) continue;
- if (current.sideEffects.dependsOn(dependsFlags)) continue;
-
- bool canBeMoved = true;
- for (final HInstruction input in current.inputs) {
- if (input.block == block) {
- canBeMoved = false;
- break;
- }
- }
- if (!canBeMoved) continue;
-
- HInstruction existing = set_.lookup(current);
- if (existing == null) {
- set_.add(current);
- } else {
- block.rewriteWithBetterUser(current, existing);
- block.remove(current);
- }
- }
- }
-}
-
-class SsaTypeConversionInserter extends HBaseVisitor
- implements OptimizationPhase {
- final String name = "SsaTypeconversionInserter";
- final Compiler compiler;
-
- SsaTypeConversionInserter(this.compiler);
-
- void visitGraph(HGraph graph) {
- visitDominatorTree(graph);
- }
-
- // Update users of [input] that are dominated by [:dominator.first:]
- // to use [TypeKnown] of [input] instead. As the type information depends
- // on the control flow, we mark the inserted [HTypeKnown] nodes as
- // non-movable.
- void insertTypePropagationForDominatedUsers(HBasicBlock dominator,
- HInstruction input,
- TypeMask convertedType) {
- Setlet<HInstruction> dominatedUsers = input.dominatedUsers(dominator.first);
- if (dominatedUsers.isEmpty) return;
-
- HTypeKnown newInput = new HTypeKnown.pinned(convertedType, input);
- dominator.addBefore(dominator.first, newInput);
- dominatedUsers.forEach((HInstruction user) {
- user.changeUse(input, newInput);
- });
- }
-
- void visitIs(HIs instruction) {
- DartType type = instruction.typeExpression;
- Element element = type.element;
- if (!instruction.isRawCheck) {
- return;
- } else if (element.isTypedef) {
- return;
- }
-
- List<HInstruction> ifUsers = <HInstruction>[];
- List<HInstruction> notIfUsers = <HInstruction>[];
-
- collectIfUsers(instruction, ifUsers, notIfUsers);
-
- if (ifUsers.isEmpty && notIfUsers.isEmpty) return;
-
- TypeMask convertedType =
- new TypeMask.nonNullSubtype(element, compiler.world);
- HInstruction input = instruction.expression;
-
- for (HIf ifUser in ifUsers) {
- insertTypePropagationForDominatedUsers(ifUser.thenBlock, input,
- convertedType);
- // TODO(ngeoffray): Also change uses for the else block on a type
- // that knows it is not of a specific type.
- }
- for (HIf ifUser in notIfUsers) {
- insertTypePropagationForDominatedUsers(ifUser.elseBlock, input,
- convertedType);
- // TODO(ngeoffray): Also change uses for the then block on a type
- // that knows it is not of a specific type.
- }
- }
-
- void visitIdentity(HIdentity instruction) {
- // At HIf(HIdentity(x, null)) strengthens x to non-null on else branch.
- HInstruction left = instruction.left;
- HInstruction right = instruction.right;
- HInstruction input;
-
- if (left.isConstantNull()) {
- input = right;
- } else if (right.isConstantNull()) {
- input = left;
- } else {
- return;
- }
-
- if (!input.instructionType.isNullable) return;
-
- List<HInstruction> ifUsers = <HInstruction>[];
- List<HInstruction> notIfUsers = <HInstruction>[];
-
- collectIfUsers(instruction, ifUsers, notIfUsers);
-
- if (ifUsers.isEmpty && notIfUsers.isEmpty) return;
-
- TypeMask nonNullType = input.instructionType.nonNullable();
-
- for (HIf ifUser in ifUsers) {
- insertTypePropagationForDominatedUsers(ifUser.elseBlock, input,
- nonNullType);
- // Uses in thenBlock are `null`, but probably not common.
- }
- for (HIf ifUser in notIfUsers) {
- insertTypePropagationForDominatedUsers(ifUser.thenBlock, input,
- nonNullType);
- // Uses in elseBlock are `null`, but probably not common.
- }
- }
-
- collectIfUsers(HInstruction instruction,
- List<HInstruction> ifUsers,
- List<HInstruction> notIfUsers) {
- for (HInstruction user in instruction.usedBy) {
- if (user is HIf) {
- ifUsers.add(user);
- } else if (user is HNot) {
- collectIfUsers(user, notIfUsers, ifUsers);
- }
- }
- }
-}
-
-/**
- * Optimization phase that tries to eliminate memory loads (for
- * example [HFieldGet]), when it knows the value stored in that memory
- * location.
- */
-class SsaLoadElimination extends HBaseVisitor implements OptimizationPhase {
- final Compiler compiler;
- final String name = "SsaLoadElimination";
- MemorySet memorySet;
- List<MemorySet> memories;
-
- SsaLoadElimination(this.compiler);
-
- void visitGraph(HGraph graph) {
- memories = new List<MemorySet>(graph.blocks.length);
- List<HBasicBlock> blocks = graph.blocks;
- for (int i = 0; i < blocks.length; i++) {
- HBasicBlock block = blocks[i];
- visitBasicBlock(block);
- if (block.successors.isNotEmpty && block.successors[0].isLoopHeader()) {
- // We've reached the ending block of a loop. Iterate over the
- // blocks of the loop again to take values that flow from that
- // ending block into account.
- for (int j = block.successors[0].id; j <= block.id; j++) {
- visitBasicBlock(blocks[j]);
- }
- }
- }
- }
-
- void visitBasicBlock(HBasicBlock block) {
- if (block.predecessors.length == 0) {
- // Entry block.
- memorySet = new MemorySet(compiler);
- } else if (block.predecessors.length == 1
- && block.predecessors[0].successors.length == 1) {
- // No need to clone, there is no other successor for
- // `block.predecessors[0]`, and this block has only one
- // predecessor. Since we are not going to visit
- // `block.predecessors[0]` again, we can just re-use its
- // [memorySet].
- memorySet = memories[block.predecessors[0].id];
- } else if (block.predecessors.length == 1) {
- // Clone the memorySet of the predecessor, because it is also used
- // by other successors of it.
- memorySet = memories[block.predecessors[0].id].clone();
- } else {
- // Compute the intersection of all predecessors.
- memorySet = memories[block.predecessors[0].id];
- for (int i = 1; i < block.predecessors.length; i++) {
- memorySet = memorySet.intersectionFor(
- memories[block.predecessors[i].id], block, i);
- }
- }
-
- memories[block.id] = memorySet;
- HInstruction instruction = block.first;
- while (instruction != null) {
- HInstruction next = instruction.next;
- instruction.accept(this);
- instruction = next;
- }
- }
-
- void visitFieldGet(HFieldGet instruction) {
- if (instruction.isNullCheck) return;
- Element element = instruction.element;
- HInstruction receiver =
- instruction.getDartReceiver(compiler).nonCheck();
- HInstruction existing = memorySet.lookupFieldValue(element, receiver);
- if (existing != null) {
- instruction.block.rewriteWithBetterUser(instruction, existing);
- instruction.block.remove(instruction);
- } else {
- memorySet.registerFieldValue(element, receiver, instruction);
- }
- }
-
- void visitFieldSet(HFieldSet instruction) {
- HInstruction receiver =
- instruction.getDartReceiver(compiler).nonCheck();
- memorySet.registerFieldValueUpdate(
- instruction.element, receiver, instruction.inputs.last);
- }
-
- void visitForeignNew(HForeignNew instruction) {
- memorySet.registerAllocation(instruction);
- int argumentIndex = 0;
- instruction.element.forEachInstanceField((_, Element member) {
- memorySet.registerFieldValue(
- member, instruction, instruction.inputs[argumentIndex++]);
- }, includeSuperAndInjectedMembers: true);
- // In case this instruction has as input non-escaping objects, we
- // need to mark these objects as escaping.
- memorySet.killAffectedBy(instruction);
- }
-
- void visitInstruction(HInstruction instruction) {
- memorySet.killAffectedBy(instruction);
- }
-
- void visitLazyStatic(HLazyStatic instruction) {
- handleStaticLoad(instruction.element, instruction);
- }
-
- void handleStaticLoad(Element element, HInstruction instruction) {
- HInstruction existing = memorySet.lookupFieldValue(element, null);
- if (existing != null) {
- instruction.block.rewriteWithBetterUser(instruction, existing);
- instruction.block.remove(instruction);
- } else {
- memorySet.registerFieldValue(element, null, instruction);
- }
- }
-
- void visitStatic(HStatic instruction) {
- handleStaticLoad(instruction.element, instruction);
- }
-
- void visitStaticStore(HStaticStore instruction) {
- memorySet.registerFieldValueUpdate(
- instruction.element, null, instruction.inputs.last);
- }
-
- void visitLiteralList(HLiteralList instruction) {
- memorySet.registerAllocation(instruction);
- memorySet.killAffectedBy(instruction);
- }
-
- void visitIndex(HIndex instruction) {
- HInstruction receiver = instruction.receiver.nonCheck();
- HInstruction existing =
- memorySet.lookupKeyedValue(receiver, instruction.index);
- if (existing != null) {
- instruction.block.rewriteWithBetterUser(instruction, existing);
- instruction.block.remove(instruction);
- } else {
- memorySet.registerKeyedValue(receiver, instruction.index, instruction);
- }
- }
-
- void visitIndexAssign(HIndexAssign instruction) {
- HInstruction receiver = instruction.receiver.nonCheck();
- memorySet.registerKeyedValueUpdate(
- receiver, instruction.index, instruction.value);
- }
-}
-
-/**
- * Holds values of memory places.
- */
-class MemorySet {
- final Compiler compiler;
-
- /**
- * Maps a field to a map of receiver to value.
- */
- final Map<Element, Map<HInstruction, HInstruction>> fieldValues =
- <Element, Map<HInstruction, HInstruction>> {};
-
- /**
- * Maps a receiver to a map of keys to value.
- */
- final Map<HInstruction, Map<HInstruction, HInstruction>> keyedValues =
- <HInstruction, Map<HInstruction, HInstruction>> {};
-
- /**
- * Set of objects that we know don't escape the current function.
- */
- final Setlet<HInstruction> nonEscapingReceivers = new Setlet<HInstruction>();
-
- MemorySet(this.compiler);
-
- /**
- * Returns whether [first] and [second] always alias to the same object.
- */
- bool mustAlias(HInstruction first, HInstruction second) {
- return first == second;
- }
-
- /**
- * Returns whether [first] and [second] may alias to the same object.
- */
- bool mayAlias(HInstruction first, HInstruction second) {
- if (mustAlias(first, second)) return true;
- if (isConcrete(first) && isConcrete(second)) return false;
- if (nonEscapingReceivers.contains(first)) return false;
- if (nonEscapingReceivers.contains(second)) return false;
- // Typed arrays of different types might have a shared buffer.
- if (couldBeTypedArray(first) && couldBeTypedArray(second)) return true;
- TypeMask intersection = first.instructionType.intersection(
- second.instructionType, compiler.world);
- if (intersection.isEmpty) return false;
- return true;
- }
-
- bool isFinal(Element element) {
- return compiler.world.fieldNeverChanges(element);
- }
-
- bool isConcrete(HInstruction instruction) {
- return instruction is HForeignNew
- || instruction is HConstant
- || instruction is HLiteralList;
- }
-
- bool couldBeTypedArray(HInstruction receiver) {
- JavaScriptBackend backend = compiler.backend;
- return backend.couldBeTypedArray(receiver.instructionType);
- }
-
- /**
- * Returns whether [receiver] escapes the current function.
- */
- bool escapes(HInstruction receiver) {
- return !nonEscapingReceivers.contains(receiver);
- }
-
- void registerAllocation(HInstruction instruction) {
- nonEscapingReceivers.add(instruction);
- }
-
- /**
- * Sets `receiver.element` to contain [value]. Kills all potential
- * places that may be affected by this update.
- */
- void registerFieldValueUpdate(Element element,
- HInstruction receiver,
- HInstruction value) {
- if (element.isNative) return; // TODO(14955): Remove this restriction?
- // [value] is being set in some place in memory, we remove it from
- // the non-escaping set.
- nonEscapingReceivers.remove(value);
- Map<HInstruction, HInstruction> map = fieldValues.putIfAbsent(
- element, () => <HInstruction, HInstruction> {});
- map.forEach((key, value) {
- if (mayAlias(receiver, key)) map[key] = null;
- });
- map[receiver] = value;
- }
-
- /**
- * Registers that `receiver.element` is now [value].
- */
- void registerFieldValue(Element element,
- HInstruction receiver,
- HInstruction value) {
- if (element.isNative) return; // TODO(14955): Remove this restriction?
- Map<HInstruction, HInstruction> map = fieldValues.putIfAbsent(
- element, () => <HInstruction, HInstruction> {});
- map[receiver] = value;
- }
-
- /**
- * Returns the value stored in `receiver.element`. Returns null if
- * we don't know.
- */
- HInstruction lookupFieldValue(Element element, HInstruction receiver) {
- Map<HInstruction, HInstruction> map = fieldValues[element];
- return (map == null) ? null : map[receiver];
- }
-
- /**
- * Kill all places that may be affected by this [instruction]. Also
- * update the set of non-escaping objects in case [instruction] has
- * non-escaping objects in its inputs.
- */
- void killAffectedBy(HInstruction instruction) {
- // Even if [instruction] does not have side effects, it may use
- // non-escaping objects and store them in a new object, which
- // make these objects escaping.
- // TODO(ngeoffray): We need a new side effect flag to know whether
- // an instruction allocates an object.
- instruction.inputs.forEach((input) {
- nonEscapingReceivers.remove(input);
- });
-
- if (instruction.sideEffects.changesInstanceProperty()
- || instruction.sideEffects.changesStaticProperty()) {
- fieldValues.forEach((element, map) {
- if (isFinal(element)) return;
- map.forEach((receiver, value) {
- if (escapes(receiver)) {
- map[receiver] = null;
- }
- });
- });
- }
-
- if (instruction.sideEffects.changesIndex()) {
- keyedValues.forEach((receiver, map) {
- if (escapes(receiver)) {
- map.forEach((index, value) {
- map[index] = null;
- });
- }
- });
- }
- }
-
- /**
- * Returns the value stored in `receiver[index]`. Returns null if
- * we don't know.
- */
- HInstruction lookupKeyedValue(HInstruction receiver, HInstruction index) {
- Map<HInstruction, HInstruction> map = keyedValues[receiver];
- return (map == null) ? null : map[index];
- }
-
- /**
- * Registers that `receiver[index]` is now [value].
- */
- void registerKeyedValue(HInstruction receiver,
- HInstruction index,
- HInstruction value) {
- Map<HInstruction, HInstruction> map = keyedValues.putIfAbsent(
- receiver, () => <HInstruction, HInstruction> {});
- map[index] = value;
- }
-
- /**
- * Sets `receiver[index]` to contain [value]. Kills all potential
- * places that may be affected by this update.
- */
- void registerKeyedValueUpdate(HInstruction receiver,
- HInstruction index,
- HInstruction value) {
- nonEscapingReceivers.remove(value);
- keyedValues.forEach((key, values) {
- if (mayAlias(receiver, key)) {
- // Typed arrays that are views of the same buffer may have different
- // offsets or element sizes, unless they are the same typed array.
- bool weakIndex = couldBeTypedArray(key) && !mustAlias(receiver, key);
- values.forEach((otherIndex, otherValue) {
- if (weakIndex || mayAlias(index, otherIndex)) {
- values[otherIndex] = null;
- }
- });
- }
- });
-
- // Typed arrays may narrow incoming values.
- if (couldBeTypedArray(receiver)) return;
-
- Map<HInstruction, HInstruction> map = keyedValues.putIfAbsent(
- receiver, () => <HInstruction, HInstruction> {});
- map[index] = value;
- }
-
- /**
- * Returns null if either [first] or [second] is null. Otherwise
- * returns [first] if [first] and [second] are equal. Otherwise
- * creates or re-uses a phi in [block] that holds [first] and [second].
- */
- HInstruction findCommonInstruction(HInstruction first,
- HInstruction second,
- HBasicBlock block,
- int predecessorIndex) {
- if (first == null || second == null) return null;
- if (first == second) return first;
- TypeMask phiType = second.instructionType.union(
- first.instructionType, compiler.world);
- if (first is HPhi && first.block == block) {
- HPhi phi = first;
- phi.addInput(second);
- phi.instructionType = phiType;
- return phi;
- } else {
- HPhi phi = new HPhi.noInputs(null, phiType);
- block.addPhi(phi);
- // Previous predecessors had the same input. A phi must have
- // the same number of inputs as its block has predecessors.
- for (int i = 0; i < predecessorIndex; i++) {
- phi.addInput(first);
- }
- phi.addInput(second);
- return phi;
- }
- }
-
- /**
- * Returns the intersection between [this] and [other].
- */
- MemorySet intersectionFor(MemorySet other,
- HBasicBlock block,
- int predecessorIndex) {
- MemorySet result = new MemorySet(compiler);
- if (other == null) return result;
-
- fieldValues.forEach((element, values) {
- var otherValues = other.fieldValues[element];
- if (otherValues == null) return;
- values.forEach((receiver, value) {
- HInstruction instruction = findCommonInstruction(
- value, otherValues[receiver], block, predecessorIndex);
- if (instruction != null) {
- result.registerFieldValue(element, receiver, instruction);
- }
- });
- });
-
- keyedValues.forEach((receiver, values) {
- var otherValues = other.keyedValues[receiver];
- if (otherValues == null) return;
- values.forEach((index, value) {
- HInstruction instruction = findCommonInstruction(
- value, otherValues[index], block, predecessorIndex);
- if (instruction != null) {
- result.registerKeyedValue(receiver, index, instruction);
- }
- });
- });
-
- nonEscapingReceivers.forEach((receiver) {
- if (other.nonEscapingReceivers.contains(receiver)) {
- result.nonEscapingReceivers.add(receiver);
- }
- });
- return result;
- }
-
- /**
- * Returns a copy of [this].
- */
- MemorySet clone() {
- MemorySet result = new MemorySet(compiler);
-
- fieldValues.forEach((element, values) {
- result.fieldValues[element] =
- new Map<HInstruction, HInstruction>.from(values);
- });
-
- keyedValues.forEach((receiver, values) {
- result.keyedValues[receiver] =
- new Map<HInstruction, HInstruction>.from(values);
- });
-
- result.nonEscapingReceivers.addAll(nonEscapingReceivers);
- return result;
- }
-}
« 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