| Index: pkg/kernel/lib/type_propagation/solver.dart
|
| diff --git a/pkg/kernel/lib/type_propagation/solver.dart b/pkg/kernel/lib/type_propagation/solver.dart
|
| deleted file mode 100644
|
| index e0deeaf207c1ef75f797f4875e889af9866aa066..0000000000000000000000000000000000000000
|
| --- a/pkg/kernel/lib/type_propagation/solver.dart
|
| +++ /dev/null
|
| @@ -1,427 +0,0 @@
|
| -// Copyright (c) 2016, 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.
|
| -library kernel.type_propagation.solver;
|
| -
|
| -import 'constraints.dart';
|
| -import 'builder.dart';
|
| -import '../class_hierarchy.dart';
|
| -import 'visualizer.dart';
|
| -import 'canonicalizer.dart';
|
| -import 'type_propagation.dart';
|
| -
|
| -class ValueVector {
|
| - List<int> values;
|
| - List<int> bitmasks;
|
| -
|
| - ValueVector(int length)
|
| - : values = new List<int>.filled(length, Solver.bottom),
|
| - bitmasks = new List<int>.filled(length, 0);
|
| -}
|
| -
|
| -/// We adopt a Hungarian-like notation in this class to distinguish variables,
|
| -/// values, and lattice points, since they are all integers.
|
| -///
|
| -/// The term "values" (plural) always refers to a lattice point.
|
| -class Solver {
|
| - final Builder builder;
|
| - final ConstraintSystem constraints;
|
| - final FieldNames canonicalizer;
|
| - final ClassHierarchy hierarchy;
|
| -
|
| - /// Maps a variable index to a values that may flow into it.
|
| - final ValueVector valuesInVariable;
|
| -
|
| - /// Maps a field index to the values that may be stored in the given field on
|
| - /// any object that escaped into a mega union.
|
| - final ValueVector valuesStoredOnEscapingObject;
|
| -
|
| - /// Maps a field index to a lattice point containing all values that may be
|
| - /// stored into the given field where the receiver is a mega union.
|
| - ///
|
| - /// This is a way to avoid propagating such stores into almost every entry
|
| - /// store location.
|
| - final ValueVector valuesStoredOnUnknownObject;
|
| -
|
| - /// Maps a lattice point to a sorted list of its ancestors in the lattice
|
| - /// (i.e. all lattice points that lie above it, and thus represent less
|
| - /// precise information).
|
| - ///
|
| - /// For this purpose, a lattice point is considered an ancestor of itself and
|
| - /// occurs as the end of its own ancestor list.
|
| - ///
|
| - /// We omit the entry for the lattice top (representing "any value") from all
|
| - /// the ancestor listss.
|
| - final List<List<int>> ancestorsOfLatticePoint;
|
| -
|
| - /// Maps a lattice point to the list of values it contains (i.e. whose leaves
|
| - /// lie below it in the lattice).
|
| - ///
|
| - /// The entries for the `Object` and `Function` lattice points are empty.
|
| - /// They are special-cased to avoid traversing a huge number of values.
|
| - final List<List<int>> valuesBelowLatticePoint;
|
| -
|
| - /// Maps a value to the lowest-indexed lattice point into which it has escaped
|
| - /// through a join operation.
|
| - ///
|
| - /// As a value escapes further up the lattice, more and more stores and loads
|
| - /// will see it as a potential target.
|
| - final List<int> valueEscape;
|
| -
|
| - /// Maps a lattice point to its lowest-indexed ancestor (possibly itself) into
|
| - /// which all of its members must escape.
|
| - ///
|
| - /// Escaping into a lattice point is transitive in the following sense:
|
| - ///
|
| - /// If a value `x` escapes into a lattice point `u`,
|
| - /// and `u` escapes into an ancestor lattice point `w`,
|
| - /// then `x` also escapes into `w`.
|
| - ///
|
| - /// The above rule also applies if the value `x` is replaced with a lattice
|
| - /// point.
|
| - ///
|
| - /// Note that some values below a given lattice point may escape further out
|
| - /// than the lattice point's own escape level.
|
| - final List<int> latticePointEscape;
|
| -
|
| - /// The lattice point containing all functions.
|
| - final int _functionLatticePoint;
|
| -
|
| - static const int bottom = -1;
|
| - static const int rootClass = 0;
|
| -
|
| - /// Lattice points with more than this number values below it are considered
|
| - /// "mega unions".
|
| - ///
|
| - /// Stores and loads are tracked less precisely on mega unions in order to
|
| - /// speed up the analysis.
|
| - ///
|
| - /// The `Object` and `Function` lattice points are always considered mega
|
| - /// unions.
|
| - static const int megaUnionLimit = 100;
|
| -
|
| - int iterations = 0;
|
| - bool _changed = false;
|
| -
|
| - static List<int> _makeIntList(int _) => <int>[];
|
| -
|
| - Visualizer get visualizer => builder.visualizer;
|
| -
|
| - Solver(Builder builder)
|
| - : this.builder = builder,
|
| - this.constraints = builder.constraints,
|
| - this.canonicalizer = builder.fieldNames,
|
| - this.hierarchy = builder.hierarchy,
|
| - this.valuesInVariable =
|
| - new ValueVector(builder.constraints.numberOfVariables),
|
| - this.ancestorsOfLatticePoint = new List<List<int>>.generate(
|
| - builder.constraints.numberOfLatticePoints, _makeIntList),
|
| - this.valuesBelowLatticePoint = new List<List<int>>.generate(
|
| - builder.constraints.numberOfLatticePoints, _makeIntList),
|
| - this._functionLatticePoint = builder.latticePointForAllFunctions,
|
| - this.valuesStoredOnEscapingObject =
|
| - new ValueVector(builder.fieldNames.length),
|
| - this.valuesStoredOnUnknownObject =
|
| - new ValueVector(builder.fieldNames.length),
|
| - this.latticePointEscape =
|
| - new List<int>.filled(builder.constraints.numberOfLatticePoints, 0),
|
| - this.valueEscape =
|
| - new List<int>.filled(builder.constraints.numberOfValues, 0) {
|
| - // Initialize the lattice and escape data.
|
| - for (int i = 1; i < constraints.numberOfLatticePoints; ++i) {
|
| - List<int> parents = constraints.parentsOfLatticePoint[i];
|
| - List<int> ancestors = ancestorsOfLatticePoint[i];
|
| - for (int j = 0; j < parents.length; ++j) {
|
| - ancestors.addAll(ancestorsOfLatticePoint[parents[j]]);
|
| - }
|
| - _sortAndRemoveDuplicates(ancestors);
|
| - ancestors.add(i);
|
| - latticePointEscape[i] = i;
|
| - }
|
| - // Initialize the set of values below in a given lattice point.
|
| - for (int value = 0; value < constraints.numberOfValues; ++value) {
|
| - int latticePoint = constraints.latticePointOfValue[value];
|
| - List<int> ancestors = ancestorsOfLatticePoint[latticePoint];
|
| - for (int j = 0; j < ancestors.length; ++j) {
|
| - int ancestor = ancestors[j];
|
| - if (ancestor == rootClass || ancestor == _functionLatticePoint) {
|
| - continue;
|
| - }
|
| - valuesBelowLatticePoint[ancestor].add(value);
|
| - }
|
| - valueEscape[value] = latticePoint;
|
| - }
|
| - }
|
| -
|
| - static void _sortAndRemoveDuplicates(List<int> list) {
|
| - list.sort();
|
| - int deleted = 0;
|
| - for (int i = 1; i < list.length; ++i) {
|
| - if (list[i] == list[i - 1]) {
|
| - ++deleted;
|
| - } else if (deleted > 0) {
|
| - list[i - deleted] = list[i];
|
| - }
|
| - }
|
| - if (deleted > 0) {
|
| - list.length -= deleted;
|
| - }
|
| - }
|
| -
|
| - /// Returns a lattice point lying above both of the given points, thus
|
| - /// guaranteed to over-approximate the set of values in both.
|
| - ///
|
| - /// If the lattice point represent classes, the upper bound is a supertype
|
| - /// that is implemented by both, and for which no more specific supertype
|
| - /// exists. If multiple such classes exist, an arbitrary but fixed choice is
|
| - /// made.
|
| - ///
|
| - /// The ambiguity means the join operator is not associative, and the analysis
|
| - /// result can therefore depend on iteration order.
|
| - //
|
| - // TODO(asgerf): I think we can fix this by introducing intersection types
|
| - // for the class pairs that are ambiguous least upper bounds. This could be
|
| - // done as a preprocessing of the constraint system.
|
| - int join(int point1, int point2) {
|
| - if (point1 == point2) return point1;
|
| - // Check if either is the bottom value (-1).
|
| - if (point1 < 0) return point2;
|
| - if (point2 < 0) return point1;
|
| - List<int> ancestorList1 = ancestorsOfLatticePoint[point1];
|
| - List<int> ancestorList2 = ancestorsOfLatticePoint[point2];
|
| - // Classes are topologically and numerically sorted, so the more specific
|
| - // supertypes always occur after the less specific ones. Traverse both
|
| - // lists from the right until a common supertype is found. Starting from
|
| - // the right ensures we can only find one of the most specific supertypes.
|
| - int i = ancestorList1.length - 1, j = ancestorList2.length - 1;
|
| - while (i >= 0 && j >= 0) {
|
| - int super1 = ancestorList1[i];
|
| - int super2 = ancestorList2[j];
|
| - if (super1 < super2) {
|
| - --j;
|
| - } else if (super1 > super2) {
|
| - --i;
|
| - } else {
|
| - // Both types have "escaped" into their common super type.
|
| - _updateEscapeIndex(point1, super1);
|
| - _updateEscapeIndex(point2, super1);
|
| - return super1;
|
| - }
|
| - }
|
| - // Both types have escaped into a completely dynamic context.
|
| - _updateEscapeIndex(point1, rootClass);
|
| - _updateEscapeIndex(point2, rootClass);
|
| - return rootClass;
|
| - }
|
| -
|
| - void _updateEscapeIndex(int point, int escapeTarget) {
|
| - if (latticePointEscape[point] > escapeTarget) {
|
| - latticePointEscape[point] = escapeTarget;
|
| - _changed = true;
|
| - }
|
| - }
|
| -
|
| - void _initializeAllocations() {
|
| - List<int> allocations = constraints.allocations;
|
| - for (int i = 0; i < allocations.length; i += 2) {
|
| - int destination = allocations[i + 1];
|
| - int valueId = allocations[i];
|
| - int point = constraints.latticePointOfValue[valueId];
|
| - valuesInVariable.values[destination] =
|
| - join(valuesInVariable.values[destination], point);
|
| - }
|
| - List<int> bitmaskInputs = constraints.bitmaskInputs;
|
| - for (int i = 0; i < bitmaskInputs.length; i += 2) {
|
| - int destination = bitmaskInputs[i + 1];
|
| - int bitmask = bitmaskInputs[i];
|
| - valuesInVariable.bitmasks[destination] |= bitmask;
|
| - }
|
| - }
|
| -
|
| - bool _isMegaUnion(int latticePoint) {
|
| - return latticePoint == rootClass ||
|
| - latticePoint == _functionLatticePoint ||
|
| - valuesBelowLatticePoint[latticePoint].length > megaUnionLimit;
|
| - }
|
| -
|
| - void solve() {
|
| - _initializeAllocations();
|
| - List<int> assignments = constraints.assignments;
|
| - List<int> loads = constraints.loads;
|
| - List<int> stores = constraints.stores;
|
| - List<int> latticePointOfValue = constraints.latticePointOfValue;
|
| - Uint31PairMap storeLocations = constraints.storeLocations;
|
| - Uint31PairMap loadLocations = constraints.loadLocations;
|
| - do {
|
| - ++iterations;
|
| - _changed = false;
|
| - for (int i = 0; i < assignments.length; i += 2) {
|
| - int destination = assignments[i + 1];
|
| - int source = assignments[i];
|
| - _update(valuesInVariable, destination, valuesInVariable, source);
|
| - }
|
| - for (int i = 0; i < stores.length; i += 3) {
|
| - int sourceVariable = stores[i + 2];
|
| - int field = stores[i + 1];
|
| - int objectVariable = stores[i];
|
| - int objectValues = valuesInVariable.values[objectVariable];
|
| - if (objectValues == bottom) continue;
|
| - if (_isMegaUnion(objectValues)) {
|
| - _update(valuesStoredOnUnknownObject, field, valuesInVariable,
|
| - sourceVariable);
|
| - } else {
|
| - // Store the value on all subtypes that can escape into this
|
| - // context or worse.
|
| - List<int> receivers = valuesBelowLatticePoint[objectValues];
|
| - for (int j = 0; j < receivers.length; ++j) {
|
| - int receiver = receivers[j];
|
| - int escape = valueEscape[receiver];
|
| - if (escape > objectValues) {
|
| - continue; // Skip receivers that have not escaped this far.
|
| - }
|
| - int location = storeLocations.lookup(receiver, field);
|
| - if (location == null) continue;
|
| - _update(
|
| - valuesInVariable, location, valuesInVariable, sourceVariable);
|
| - }
|
| - }
|
| - }
|
| - for (int i = 0; i < loads.length; i += 3) {
|
| - int destination = loads[i + 2];
|
| - int field = loads[i + 1];
|
| - int objectVariable = loads[i];
|
| - int objectValues = valuesInVariable.values[objectVariable];
|
| - if (objectValues == bottom) continue;
|
| - if (_isMegaUnion(objectValues)) {
|
| - // Receiver is unknown. Take the value out of the tarpit.
|
| - _update(valuesInVariable, destination, valuesStoredOnEscapingObject,
|
| - field);
|
| - } else {
|
| - // Load the values stored on all the subtypes that can escape into
|
| - // this context or worse.
|
| - List<int> receivers = valuesBelowLatticePoint[objectValues];
|
| - for (int j = 0; j < receivers.length; ++j) {
|
| - int receiver = receivers[j];
|
| - int escape = valueEscape[receiver];
|
| - if (escape > objectValues) {
|
| - continue; // Skip receivers that have not escaped this far.
|
| - }
|
| - int location = loadLocations.lookup(receiver, field);
|
| - if (location == null) continue;
|
| - _update(valuesInVariable, destination, valuesInVariable, location);
|
| - }
|
| - }
|
| - }
|
| - // Apply the transitive escape rule on the lattice.
|
| - for (int point = 0; point < latticePointEscape.length; ++point) {
|
| - int oldEscape = latticePointEscape[point];
|
| - if (oldEscape == point) continue;
|
| - List<int> ancestors = ancestorsOfLatticePoint[point];
|
| - int newEscape = oldEscape;
|
| - for (int j = 0; j < ancestors.length; ++j) {
|
| - int ancestor = ancestors[j];
|
| - if (ancestor < oldEscape) continue;
|
| - int superEscape = latticePointEscape[ancestor];
|
| - if (superEscape < newEscape) {
|
| - newEscape = superEscape;
|
| - }
|
| - }
|
| - if (oldEscape != newEscape) {
|
| - latticePointEscape[point] = newEscape;
|
| - _changed = true;
|
| - }
|
| - }
|
| - // Update the escape level of every value.
|
| - for (int i = 0; i < latticePointOfValue.length; ++i) {
|
| - int value = i;
|
| - int latticePoint = latticePointOfValue[value];
|
| - int oldEscape = valueEscape[value];
|
| - int newEscape = latticePointEscape[latticePoint];
|
| - if (newEscape < oldEscape) {
|
| - valueEscape[value] = newEscape;
|
| - _changed = true;
|
| - }
|
| - }
|
| - // Handle stores on escaping objects.
|
| - List<int> storeLocationList = constraints.storeLocationList;
|
| - for (int i = 0; i < storeLocationList.length; i += 3) {
|
| - int variable = storeLocationList[i + 2];
|
| - int field = storeLocationList[i + 1];
|
| - int objectValue = storeLocationList[i];
|
| - int escape = valueEscape[objectValue];
|
| - if (_isMegaUnion(escape)) {
|
| - _update(
|
| - valuesInVariable, variable, valuesStoredOnUnknownObject, field);
|
| - }
|
| - }
|
| - // Handle loads from escaping objects.
|
| - List<int> loadLocationList = constraints.loadLocationList;
|
| - for (int i = 0; i < loadLocationList.length; i += 3) {
|
| - int variable = loadLocationList[i + 2];
|
| - int field = loadLocationList[i + 1];
|
| - int objectValue = loadLocationList[i];
|
| - int escape = valueEscape[objectValue];
|
| - if (_isMegaUnion(escape)) {
|
| - _update(
|
| - valuesStoredOnEscapingObject, field, valuesInVariable, variable);
|
| - }
|
| - }
|
| - } while (_changed);
|
| -
|
| - // Propagate to sinks.
|
| - // This is done outside the fixed-point iteration so the sink-join does
|
| - // not cause values to be considered escaping.
|
| - List<int> sinks = constraints.sinks;
|
| - for (int i = 0; i < sinks.length; i += 2) {
|
| - int destination = sinks[i + 1];
|
| - int source = sinks[i];
|
| - _update(valuesInVariable, destination, valuesInVariable, source);
|
| - }
|
| - }
|
| -
|
| - void _update(ValueVector destinationVector, int destinationIndex,
|
| - ValueVector sourceVector, int sourceIndex) {
|
| - int oldValues = destinationVector.values[destinationIndex];
|
| - int inputValues = sourceVector.values[sourceIndex];
|
| - int newValues = join(inputValues, oldValues);
|
| - if (newValues != oldValues) {
|
| - destinationVector.values[destinationIndex] = newValues;
|
| - _changed = true;
|
| - }
|
| - int oldBits = destinationVector.bitmasks[destinationIndex];
|
| - int inputBits = sourceVector.bitmasks[sourceIndex];
|
| - int newBits = inputBits | oldBits;
|
| - if (newBits != oldBits) {
|
| - destinationVector.bitmasks[destinationIndex] = newBits;
|
| - _changed = true;
|
| - }
|
| - }
|
| -
|
| - /// Returns the index of a lattice point containing all values that can flow
|
| - /// into the given variable, or [bottom] if nothing can flow into the
|
| - /// variable.
|
| - int getVariableValue(int variable) {
|
| - return valuesInVariable.values[variable];
|
| - }
|
| -
|
| - int getVariableBitmask(int variable) {
|
| - return valuesInVariable.bitmasks[variable];
|
| - }
|
| -
|
| - /// Returns the lowest-indexed lattice point into which the given value can
|
| - /// escape.
|
| - int getEscapeContext(int value) {
|
| - return valueEscape[value];
|
| - }
|
| -
|
| - InferredValue getValueInferredForVariable(int variable) {
|
| - assert(variable != null);
|
| - int latticePoint = valuesInVariable.values[variable];
|
| - int bitmask = valuesInVariable.bitmasks[variable];
|
| - if (latticePoint == bottom) {
|
| - return new InferredValue(null, BaseClassKind.None, bitmask);
|
| - }
|
| - InferredValue value = builder.getBaseTypeOfLatticePoint(latticePoint);
|
| - return value.withBitmask(bitmask);
|
| - }
|
| -}
|
|
|