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); |
- } |
-} |