Chromium Code Reviews| Index: pkg/compiler/lib/src/cps_ir/constant_propagation.dart |
| diff --git a/pkg/compiler/lib/src/cps_ir/constant_propagation.dart b/pkg/compiler/lib/src/cps_ir/constant_propagation.dart |
| index c9adf089d055cb93124b59faaef4fa3db00dd64e..b965ebc0011ec89795979f932dc79846e58b50a9 100644 |
| --- a/pkg/compiler/lib/src/cps_ir/constant_propagation.dart |
| +++ b/pkg/compiler/lib/src/cps_ir/constant_propagation.dart |
| @@ -4,24 +4,109 @@ |
| part of dart2js.optimizers; |
| +abstract class TypeSystem<T> { |
| + T get dynamicType; |
| + T get typeType; |
| + T get functionType; |
| + T get boolType; |
| + T get intType; |
| + T get stringType; |
| + T get listType; |
| + T get mapType; |
| + |
| + T getReturnType(FunctionElement element); |
| + T getParameterType(ParameterElement element); |
| + bool areEqual(T a, T b); |
| + bool areAssignable(T a, T b); |
| + T join(T a, T b); |
| + T typeOf(ConstantValue constant); |
| +} |
| + |
| +class TypeMaskSystem implements TypeSystem<TypeMask> { |
| + final TypesTask inferrer; |
| + final ClassWorld classWorld; |
| + |
| + TypeMask get dynamicType => inferrer.dynamicType; |
| + TypeMask get typeType => inferrer.typeType; |
| + TypeMask get functionType => inferrer.functionType; |
| + TypeMask get boolType => inferrer.boolType; |
| + TypeMask get intType => inferrer.intType; |
| + TypeMask get stringType => inferrer.stringType; |
| + TypeMask get listType => inferrer.listType; |
| + TypeMask get mapType => inferrer.mapType; |
| + |
| + // TODO(karlklose): the map should be per continuation. |
| + Map<Node, TypeMask> map = <Node, TypeMask>{}; |
| + |
| + TypeMaskSystem(dart2js.Compiler compiler) |
| + : inferrer = compiler.typesTask, |
| + classWorld = compiler.world; |
| + |
| + TypeMask getType(Node node) => map[node]; |
| + |
| + setType(Primitive node, TypeMask type) => map[node] = type; |
| + |
| + TypeMask getParameterType(ParameterElement parameter) { |
| + return inferrer.getGuaranteedTypeOfElement(parameter); |
| + } |
| + |
| + TypeMask getReturnType(FunctionElement function) { |
| + return inferrer.getGuaranteedReturnTypeOfElement(function); |
| + } |
| + |
| + @override |
| + bool areEqual(TypeMask a, TypeMask b) { |
| + return identical(a, b) || |
| + a.isInMask(b, classWorld) || |
|
Kevin Millikin (Google)
2014/12/12 11:00:23
Typo: || should be &&.
karlklose
2014/12/12 11:43:48
Obsolete, removed the method from the TypeSystem i
|
| + a.containsMask(b, classWorld); |
| + } |
| + |
| + @override |
| + bool areAssignable(TypeMask a, TypeMask b) { |
| + return a.containsMask(b, classWorld) || b.containsMask(a, classWorld); |
| + } |
| + |
| + @override |
| + TypeMask join(TypeMask a, TypeMask b) { |
| + return a.union(b, classWorld); |
| + } |
| + |
| + @override |
| + TypeMask typeOf(ConstantValue constant) { |
| + return constant.computeMask(inferrer.compiler); |
| + } |
| +} |
| + |
| +typedef void InternalErrorFunction(Spannable location, String message); |
| + |
| /** |
| - * Propagates constants throughout the IR, and replaces branches with fixed |
| - * jumps as well as side-effect free expressions with known constant results. |
| + * Propagates types (including value types for constants) throughout the IR, and |
| + * replaces branches with fixed jumps as well as side-effect free expressions |
| + * with known constant results. |
| + * |
| * Should be followed by the [ShrinkingReducer] pass. |
| * |
| * Implemented according to 'Constant Propagation with Conditional Branches' |
| * by Wegman, Zadeck. |
| */ |
| -class ConstantPropagator extends Pass { |
| - |
| - // Required for type determination in analysis of TypeOperator expressions. |
| +class TypePropagator<T> extends Pass { |
| + // TODO(karlklose): remove reference to _compiler. It is currently used to |
| + // compute [TypeMask]s. |
| final dart2js.Compiler _compiler; |
| // The constant system is used for evaluation of expressions with constant |
| // arguments. |
| final dart2js.ConstantSystem _constantSystem; |
| + final TypeSystem _typeSystem; |
| + final InternalErrorFunction _internalError; |
| + final Map<Node, _AbstractValue> _types; |
| + |
| - ConstantPropagator(this._compiler, this._constantSystem); |
| + TypePropagator(this._compiler, |
| + this._constantSystem, |
| + this._typeSystem, |
| + this._internalError) |
| + : _types = <Node, _AbstractValue>{}; |
| void _rewriteExecutableDefinition(ExecutableDefinition root) { |
| // Set all parent pointers. |
| @@ -29,17 +114,20 @@ class ConstantPropagator extends Pass { |
| // Analyze. In this phase, the entire term is analyzed for reachability |
| // and the constant status of each expression. |
|
Kevin Millikin (Google)
2014/12/12 11:00:23
'constant status' ==> 'abstract value'?
karlklose
2014/12/12 11:43:48
Done.
|
| + _ConstPropagationVisitor<T> analyzer = new _ConstPropagationVisitor<T>( |
| + _constantSystem, |
| + _typeSystem, |
| + _types, |
| + _internalError, |
| + _compiler); |
| - _ConstPropagationVisitor analyzer = |
| - new _ConstPropagationVisitor(_compiler, _constantSystem); |
| analyzer.analyze(root); |
| // Transform. Uses the data acquired in the previous analysis phase to |
| // replace branches with fixed targets and side-effect-free expressions |
| // with constant results. |
| - |
| _TransformingVisitor transformer = new _TransformingVisitor( |
| - analyzer.reachableNodes, analyzer.node2value); |
| + analyzer.reachableNodes, analyzer.values, _internalError); |
| transformer.transform(root); |
| } |
| @@ -53,6 +141,7 @@ class ConstantPropagator extends Pass { |
| _rewriteExecutableDefinition(root); |
| } |
| + getType(Node node) => _types[node]; |
| } |
| /** |
| @@ -60,11 +149,12 @@ class ConstantPropagator extends Pass { |
| * actual transformations on the CPS graph. |
| */ |
| class _TransformingVisitor extends RecursiveVisitor { |
| - |
| final Set<Node> reachable; |
| - final Map<Node, _ConstnessLattice> node2value; |
| + final Map<Node, _AbstractValue> values; |
| - _TransformingVisitor(this.reachable, this.node2value); |
| + final InternalErrorFunction internalError; |
| + |
| + _TransformingVisitor(this.reachable, this.values, this.internalError); |
| void transform(ExecutableDefinition root) { |
| visit(root); |
| @@ -76,16 +166,15 @@ class _TransformingVisitor extends RecursiveVisitor { |
| LetPrim constifyExpression(Expression node, |
| Continuation continuation, |
| void unlink()) { |
| - _ConstnessLattice cell = node2value[node]; |
| - if (cell == null || !cell.isConstant) { |
| + _AbstractValue value = values[node]; |
| + if (value == null || !value.isConstant) { |
| return null; |
| } |
| assert(continuation.parameters.length == 1); |
| // Set up the replacement structure. |
| - |
| - PrimitiveConstantValue primitiveConstant = cell.constant; |
| + PrimitiveConstantValue primitiveConstant = value.constant; |
| ConstantExpression constExp = |
| new PrimitiveConstantExpression(primitiveConstant); |
| Constant constant = new Constant(constExp); |
| @@ -207,7 +296,7 @@ class _TransformingVisitor extends RecursiveVisitor { |
| * const-ness as well as reachability, both of which are used in the subsequent |
| * transformation pass. |
| */ |
| -class _ConstPropagationVisitor extends Visitor { |
| +class _ConstPropagationVisitor<T> extends Visitor { |
| // The node worklist stores nodes that are both reachable and need to be |
| // processed, but have not been processed yet. Using a worklist avoids deep |
| // recursion. |
| @@ -224,15 +313,42 @@ class _ConstPropagationVisitor extends Visitor { |
| // since their lattice value has changed. |
| final Set<Definition> defWorkset = new Set<Definition>(); |
| - final dart2js.Compiler compiler; |
| final dart2js.ConstantSystem constantSystem; |
| + final TypeSystem typeSystem; |
| + final InternalErrorFunction internalError; |
| + final Compiler compiler; |
| + |
| + _AbstractValue unknownDynamic; |
| + |
| + _AbstractValue unknown([T t]) { |
| + if (t == null) { |
| + return unknownDynamic; |
| + } else { |
| + return new _AbstractValue.unknown(t); |
| + } |
| + } |
| + |
| + _AbstractValue nonConst([T type]) { |
| + if (type == null) { |
| + type = typeSystem.dynamicType; |
| + } |
| + return new _AbstractValue.nonConst(type); |
| + } |
| + |
| + _AbstractValue constantValue(ConstantValue constant, T type) { |
| + return new _AbstractValue(constant, type); |
| + } |
| // Stores the current lattice value for nodes. Note that it contains not only |
| // definitions as keys, but also expressions such as method invokes. |
| // Access through [getValue] and [setValue]. |
| - final Map<Node, _ConstnessLattice> node2value = <Node, _ConstnessLattice>{}; |
| + final Map<Node, _AbstractValue> values; |
| - _ConstPropagationVisitor(this.compiler, this.constantSystem); |
| + _ConstPropagationVisitor(this.constantSystem, TypeSystem typeSystem, |
| + this.values, |
| + this.internalError, this.compiler) |
| + : this.unknownDynamic = new _AbstractValue.unknown(typeSystem.dynamicType), |
| + this.typeSystem = typeSystem; |
| void analyze(ExecutableDefinition root) { |
| reachableNodes.clear(); |
| @@ -276,17 +392,17 @@ class _ConstPropagationVisitor extends Visitor { |
| /// Returns the lattice value corresponding to [node], defaulting to unknown. |
| /// |
| /// Never returns null. |
| - _ConstnessLattice getValue(Node node) { |
| - _ConstnessLattice value = node2value[node]; |
| - return (value == null) ? _ConstnessLattice.Unknown : value; |
| + _AbstractValue getValue(Node node) { |
| + _AbstractValue value = values[node]; |
| + return (value == null) ? unknown() : value; |
| } |
| /// Joins the passed lattice [updateValue] to the current value of [node], |
| /// and adds it to the definition work set if it has changed and [node] is |
| /// a definition. |
| - void setValue(Node node, _ConstnessLattice updateValue) { |
| - _ConstnessLattice oldValue = getValue(node); |
| - _ConstnessLattice newValue = updateValue.join(oldValue); |
| + void setValue(Node node, _AbstractValue updateValue) { |
| + _AbstractValue oldValue = getValue(node); |
| + _AbstractValue newValue = updateValue.join(oldValue, typeSystem); |
| if (oldValue == newValue) { |
| return; |
| } |
| @@ -294,7 +410,7 @@ class _ConstPropagationVisitor extends Visitor { |
| // Values may only move in the direction UNKNOWN -> CONSTANT -> NONCONST. |
| assert(newValue.kind >= oldValue.kind); |
| - node2value[node] = newValue; |
| + values[node] = newValue; |
| if (node is Definition) { |
| defWorkset.add(node); |
| } |
| @@ -303,7 +419,7 @@ class _ConstPropagationVisitor extends Visitor { |
| // -------------------------- Visitor overrides ------------------------------ |
| void visitNode(Node node) { |
| - compiler.internalError(NO_LOCATION_SPANNABLE, |
| + internalError(NO_LOCATION_SPANNABLE, |
| "_ConstPropagationVisitor is stale, add missing visit overrides"); |
| } |
| @@ -336,7 +452,11 @@ class _ConstPropagationVisitor extends Visitor { |
| assert(cont.parameters.length == 1); |
| Parameter returnValue = cont.parameters[0]; |
| - setValue(returnValue, _ConstnessLattice.NonConst); |
| + Entity target = node.target; |
| + T returnType = target is FieldElement |
| + ? typeSystem.dynamicType |
| + : typeSystem.getReturnType(node.target); |
| + setValue(returnValue, nonConst(returnType)); |
| } |
| void visitInvokeContinuation(InvokeContinuation node) { |
| @@ -347,7 +467,7 @@ class _ConstPropagationVisitor extends Visitor { |
| // continuation. Note that this is effectively a phi node in SSA terms. |
| for (int i = 0; i < node.arguments.length; i++) { |
| Definition def = node.arguments[i].definition; |
| - _ConstnessLattice cell = getValue(def); |
| + _AbstractValue cell = getValue(def); |
| setValue(cont.parameters[i], cell); |
| } |
| } |
| @@ -358,13 +478,13 @@ class _ConstPropagationVisitor extends Visitor { |
| /// Sets the value of both the current node and the target continuation |
| /// parameter. |
| - void setValues(_ConstnessLattice updateValue) { |
| + void setValues(_AbstractValue updateValue) { |
| setValue(node, updateValue); |
| Parameter returnValue = cont.parameters[0]; |
| setValue(returnValue, updateValue); |
| } |
| - _ConstnessLattice lhs = getValue(node.receiver.definition); |
| + _AbstractValue lhs = getValue(node.receiver.definition); |
| if (lhs.isUnknown) { |
| // This may seem like a missed opportunity for evaluating short-circuiting |
| // boolean operations; we are currently skipping these intentionally since |
| @@ -374,11 +494,11 @@ class _ConstPropagationVisitor extends Visitor { |
| // a type-check (in checked mode) are still executed. |
| return; // And come back later. |
| } else if (lhs.isNonConst) { |
| - setValues(_ConstnessLattice.NonConst); |
| + setValues(nonConst()); |
| return; |
| } else if (!node.selector.isOperator) { |
| // TODO(jgruber): Handle known methods on constants such as String.length. |
| - setValues(_ConstnessLattice.NonConst); |
| + setValues(nonConst()); |
| return; |
| } |
| @@ -398,7 +518,7 @@ class _ConstPropagationVisitor extends Visitor { |
| } else if (node.selector.argumentCount == 1) { |
| // Binary operator. |
| - _ConstnessLattice rhs = getValue(node.arguments[0].definition); |
| + _AbstractValue rhs = getValue(node.arguments[0].definition); |
| if (!rhs.isConstant) { |
| setValues(rhs); |
| return; |
| @@ -412,9 +532,12 @@ class _ConstPropagationVisitor extends Visitor { |
| // Update value of the continuation parameter. Again, this is effectively |
| // a phi. |
| - |
| - setValues((result == null) ? |
| - _ConstnessLattice.NonConst : new _ConstnessLattice(result)); |
| + if (result == null) { |
| + setValues(nonConst()); |
| + } else { |
| + T type = typeSystem.typeOf(result); |
| + setValues(new _AbstractValue(result, type)); |
| + } |
| } |
| void visitInvokeSuperMethod(InvokeSuperMethod node) { |
| @@ -423,7 +546,8 @@ class _ConstPropagationVisitor extends Visitor { |
| assert(cont.parameters.length == 1); |
| Parameter returnValue = cont.parameters[0]; |
| - setValue(returnValue, _ConstnessLattice.NonConst); |
| + // TODO(karlklose): lookup the function and get ites return type. |
| + setValue(returnValue, nonConst()); |
| } |
| void visitInvokeConstructor(InvokeConstructor node) { |
| @@ -432,14 +556,14 @@ class _ConstPropagationVisitor extends Visitor { |
| assert(cont.parameters.length == 1); |
| Parameter returnValue = cont.parameters[0]; |
| - setValue(returnValue, _ConstnessLattice.NonConst); |
| + setValue(returnValue, nonConst()); |
| } |
| void visitConcatenateStrings(ConcatenateStrings node) { |
| Continuation cont = node.continuation.definition; |
| setReachable(cont); |
| - void setValues(_ConstnessLattice updateValue) { |
| + void setValues(_AbstractValue updateValue) { |
| setValue(node, updateValue); |
| Parameter returnValue = cont.parameters[0]; |
| setValue(returnValue, updateValue); |
| @@ -455,6 +579,7 @@ class _ConstPropagationVisitor extends Visitor { |
| return constant != null && constant.value.isString; |
| }); |
| + T type = typeSystem.stringType; |
| assert(cont.parameters.length == 1); |
| if (allStringConstants) { |
| // All constant, we can concatenate ourselves. |
| @@ -465,15 +590,15 @@ class _ConstPropagationVisitor extends Visitor { |
| }); |
| LiteralDartString dartString = new LiteralDartString(allStrings.join()); |
| ConstantValue constant = new StringConstantValue(dartString); |
| - setValues(new _ConstnessLattice(constant)); |
| + setValues(new _AbstractValue(constant, type)); |
| } else { |
| - setValues(_ConstnessLattice.NonConst); |
| + setValues(nonConst(type)); |
| } |
| } |
| void visitBranch(Branch node) { |
| IsTrue isTrue = node.condition; |
| - _ConstnessLattice conditionCell = getValue(isTrue.value.definition); |
| + _AbstractValue conditionCell = getValue(isTrue.value.definition); |
| if (conditionCell.isUnknown) { |
| return; // And come back later. |
| @@ -487,7 +612,7 @@ class _ConstPropagationVisitor extends Visitor { |
| // TODO(jgruber): Default to false in unchecked mode. |
| setReachable(node.trueContinuation.definition); |
| setReachable(node.falseContinuation.definition); |
| - setValue(isTrue.value.definition, _ConstnessLattice.NonConst); |
| + setValue(isTrue.value.definition, nonConst(typeSystem.boolType)); |
| } else if (conditionCell.isConstant && |
| conditionCell.constant.isBool) { |
| BoolConstantValue boolConstant = conditionCell.constant; |
| @@ -500,7 +625,7 @@ class _ConstPropagationVisitor extends Visitor { |
| Continuation cont = node.continuation.definition; |
| setReachable(cont); |
| - void setValues(_ConstnessLattice updateValue) { |
| + void setValues(_AbstractValue updateValue) { |
| setValue(node, updateValue); |
| Parameter returnValue = cont.parameters[0]; |
| setValue(returnValue, updateValue); |
| @@ -508,35 +633,37 @@ class _ConstPropagationVisitor extends Visitor { |
| if (node.isTypeCast) { |
| // TODO(jgruber): Add support for `as` casts. |
| - setValues(_ConstnessLattice.NonConst); |
| + setValues(nonConst()); |
| } |
| - _ConstnessLattice cell = getValue(node.receiver.definition); |
| + _AbstractValue cell = getValue(node.receiver.definition); |
| if (cell.isUnknown) { |
| return; // And come back later. |
| } else if (cell.isNonConst) { |
| - setValues(_ConstnessLattice.NonConst); |
| + setValues(nonConst(cell.type)); |
| } else if (node.type.kind == types.TypeKind.INTERFACE) { |
| // Receiver is a constant, perform is-checks at compile-time. |
| types.InterfaceType checkedType = node.type; |
| ConstantValue constant = cell.constant; |
| + // TODO(karlklose): remove call to computeType. |
| types.DartType constantType = constant.computeType(compiler); |
| - _ConstnessLattice result = _ConstnessLattice.NonConst; |
| + T type = typeSystem.boolType; |
| + _AbstractValue result; |
| if (constant.isNull && |
| checkedType.element != compiler.nullClass && |
| checkedType.element != compiler.objectClass) { |
| // `(null is Type)` is true iff Type is in { Null, Object }. |
| - result = new _ConstnessLattice(new FalseConstantValue()); |
| + result = constantValue(new FalseConstantValue(), type); |
| } else { |
| // Otherwise, perform a standard subtype check. |
| - result = new _ConstnessLattice( |
| + result = constantValue( |
| constantSystem.isSubtype(compiler, constantType, checkedType) |
| ? new TrueConstantValue() |
| - : new FalseConstantValue()); |
| + : new FalseConstantValue(), |
| + type); |
| } |
| - |
| setValues(result); |
| } |
| } |
| @@ -554,58 +681,62 @@ class _ConstPropagationVisitor extends Visitor { |
| void visitLiteralList(LiteralList node) { |
| // Constant lists are translated into (Constant ListConstant(...)) IR nodes, |
| // and thus LiteralList nodes are NonConst. |
| - setValue(node, _ConstnessLattice.NonConst); |
| + setValue(node, nonConst(typeSystem.listType)); |
| } |
| void visitLiteralMap(LiteralMap node) { |
| // Constant maps are translated into (Constant MapConstant(...)) IR nodes, |
| // and thus LiteralMap nodes are NonConst. |
| - setValue(node, _ConstnessLattice.NonConst); |
| + setValue(node, nonConst(typeSystem.mapType)); |
| } |
| void visitConstant(Constant node) { |
| - setValue(node, new _ConstnessLattice(node.value)); |
| + ConstantValue value = node.value; |
| + setValue(node, constantValue(value, typeSystem.typeOf(value))); |
| } |
| void visitThis(This node) { |
| - setValue(node, _ConstnessLattice.NonConst); |
| + // TODO(karlklose): Add the type. |
| + setValue(node, nonConst()); |
| } |
| void visitReifyTypeVar(ReifyTypeVar node) { |
| - setValue(node, _ConstnessLattice.NonConst); |
| + setValue(node, nonConst(typeSystem.typeType)); |
| } |
| void visitCreateFunction(CreateFunction node) { |
| setReachable(node.definition); |
| ConstantValue constant = |
| new FunctionConstantValue(node.definition.element); |
| - setValue(node, new _ConstnessLattice(constant)); |
| + setValue(node, constantValue(constant, typeSystem.functionType)); |
| } |
| void visitGetClosureVariable(GetClosureVariable node) { |
| - setValue(node, _ConstnessLattice.NonConst); |
| + setValue(node, nonConst()); |
| } |
| void visitClosureVariable(ClosureVariable node) { |
| } |
| void visitParameter(Parameter node) { |
| + T type = typeSystem.getParameterType(node.hint); |
| if (node.parent is FunctionDefinition) { |
| // Functions may escape and thus their parameters must be initialized to |
| // NonConst. |
| - setValue(node, _ConstnessLattice.NonConst); |
| + setValue(node, nonConst(type)); |
| } else if (node.parent is Continuation) { |
| // Continuations on the other hand are local, and parameters are |
| // initialized to Unknown. |
| - setValue(node, _ConstnessLattice.Unknown); |
| + setValue(node, unknown()); |
| } else { |
| - compiler.internalError(node.hint, "Unexpected parent of Parameter"); |
| + internalError(node.hint, "Unexpected parent of Parameter"); |
| } |
| } |
| void visitContinuation(Continuation node) { |
| node.parameters.forEach((Parameter p) { |
| - setValue(p, _ConstnessLattice.Unknown); |
| + // TODO(karlklose): join parameter types from use sites. |
| + setValue(p, unknown()); |
| defWorkset.add(p); |
| }); |
| @@ -624,22 +755,29 @@ class _ConstPropagationVisitor extends Visitor { |
| // JavaScript specific nodes. |
| void visitIdentical(Identical node) { |
| - _ConstnessLattice leftConst = getValue(node.left.definition); |
| - _ConstnessLattice rightConst = getValue(node.left.definition); |
| + _AbstractValue leftConst = getValue(node.left.definition); |
| + _AbstractValue rightConst = getValue(node.right.definition); |
| ConstantValue leftValue = leftConst.constant; |
| ConstantValue rightValue = rightConst.constant; |
| if (leftConst.isUnknown || rightConst.isUnknown) { |
| // Come back later. |
| return; |
| } else if (!leftConst.isConstant || !rightConst.isConstant) { |
| - setValue(node, _ConstnessLattice.NonConst); |
| + T leftType = leftConst.type; |
| + T rightType = rightConst.type; |
| + if (!typeSystem.areAssignable(leftType, rightType)) { |
| + setValue(node, |
| + constantValue(new FalseConstantValue(), typeSystem.boolType)); |
| + } else { |
| + setValue(node, nonConst(typeSystem.boolType)); |
| + } |
| } else if (leftValue.isPrimitive && rightValue.isPrimitive) { |
| assert(leftConst.isConstant && rightConst.isConstant); |
| PrimitiveConstantValue left = leftValue; |
| PrimitiveConstantValue right = rightValue; |
| ConstantValue result = |
| new BoolConstantValue(left.primitiveValue == right.primitiveValue); |
| - setValue(node, new _ConstnessLattice(result)); |
| + setValue(node, new _AbstractValue(result, typeSystem.boolType)); |
| } |
| } |
| } |
| @@ -648,58 +786,86 @@ class _ConstPropagationVisitor extends Visitor { |
| /// UNKNOWN: may be some as yet undetermined constant. |
| /// CONSTANT: is a constant as stored in the local field. |
| /// NONCONST: not a constant. |
| -class _ConstnessLattice { |
| +class _AbstractValue<T> { |
| static const int UNKNOWN = 0; |
| static const int CONSTANT = 1; |
| static const int NONCONST = 2; |
| final int kind; |
| final ConstantValue constant; |
| + final T type; |
| - static final _ConstnessLattice Unknown = |
| - new _ConstnessLattice._internal(UNKNOWN, null); |
| - static final _ConstnessLattice NonConst = |
| - new _ConstnessLattice._internal(NONCONST, null); |
| - |
| - _ConstnessLattice._internal(this.kind, this.constant); |
| - _ConstnessLattice(this.constant) : kind = CONSTANT { |
| - assert(this.constant != null); |
| + _AbstractValue._internal(this.kind, this.constant, this.type) { |
| + assert(kind != CONSTANT || constant != null); |
| + assert(type != null); |
| } |
| + _AbstractValue(ConstantValue constant, T type) |
| + : this._internal(CONSTANT, constant, type); |
| + |
| + _AbstractValue.unknown(T type) |
| + : this._internal(UNKNOWN, null, type); |
| + |
| + _AbstractValue.nonConst(T type) |
| + : this._internal(NONCONST, null, type); |
| + |
| bool get isUnknown => (kind == UNKNOWN); |
| bool get isConstant => (kind == CONSTANT); |
| bool get isNonConst => (kind == NONCONST); |
| - int get hashCode => kind | (constant.hashCode << 2); |
| - bool operator==(_ConstnessLattice that) => |
| - (that.kind == this.kind && that.constant == this.constant); |
| + int get hashCode { |
| + return kind | (constant.hashCode * 5) | type.hashCode * 7; |
| + } |
| + |
| + bool operator ==(_AbstractValue that) { |
| + return that.kind == this.kind && |
| + that.constant == this.constant && |
| + that.type == this.type; |
| + } |
| String toString() { |
| switch (kind) { |
| case UNKNOWN: return "Unknown"; |
| - case CONSTANT: return "Constant: $constant"; |
| - case NONCONST: return "Non-constant"; |
| + case CONSTANT: return "Constant: $constant: $type"; |
| + case NONCONST: return "Non-constant: $type"; |
| default: assert(false); |
| } |
| return null; |
| } |
| /// Compute the join of two values in the lattice. |
| - _ConstnessLattice join(_ConstnessLattice that) { |
| + _AbstractValue join(_AbstractValue that, TypeSystem typeSystem) { |
| + bool isDynamic(T type) { |
| + return typeSystem.areEqual(type, typeSystem.dynamicType); |
| + } |
| + |
| assert(that != null); |
| - if (this.isNonConst || that.isUnknown) { |
| - return this; |
| - } |
| + if (isDynamic(this.type) && isDynamic(that.type)) { |
| + if (this.isNonConst || that.isUnknown) { |
| + return this; |
| + } |
| - if (this.isUnknown || that.isNonConst) { |
| - return that; |
| + if (this.isUnknown || that.isNonConst) { |
| + return that; |
| + } |
| + |
| + if (this.constant == that.constant) { |
| + return this; |
| + } |
| + |
| + return new _AbstractValue.nonConst(typeSystem.dynamicType); |
| } |
| - if (this.constant == that.constant) { |
| + if (this.isUnknown) { |
| + return that; |
| + } else if (that.isUnknown) { |
| + return this; |
| + } else if (this.isConstant && that.isConstant && |
| + this.constant == that.constant) { |
| return this; |
| + } else { |
| + return new _AbstractValue.nonConst(typeSystem.join(this.type, that.type)); |
|
Kevin Millikin (Google)
2014/12/12 11:00:23
If we drop the optimization to avoid joining dynam
karlklose
2014/12/12 11:43:48
Done.
|
| } |
| - |
| - return NonConst; |
| } |
| } |