Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of dart2js.optimizers; | 5 part of dart2js.optimizers; |
| 6 | 6 |
| 7 abstract class TypeSystem<T> { | |
| 8 T get dynamicType; | |
| 9 T get typeType; | |
| 10 T get functionType; | |
| 11 T get boolType; | |
| 12 T get intType; | |
| 13 T get stringType; | |
| 14 T get listType; | |
| 15 T get mapType; | |
| 16 | |
| 17 T getReturnType(FunctionElement element); | |
| 18 T getParameterType(ParameterElement element); | |
| 19 bool areEqual(T a, T b); | |
| 20 bool areAssignable(T a, T b); | |
| 21 T join(T a, T b); | |
| 22 T typeOf(ConstantValue constant); | |
| 23 } | |
| 24 | |
| 25 class TypeMaskSystem implements TypeSystem<TypeMask> { | |
| 26 final TypesTask inferrer; | |
| 27 final ClassWorld classWorld; | |
| 28 | |
| 29 TypeMask get dynamicType => inferrer.dynamicType; | |
| 30 TypeMask get typeType => inferrer.typeType; | |
| 31 TypeMask get functionType => inferrer.functionType; | |
| 32 TypeMask get boolType => inferrer.boolType; | |
| 33 TypeMask get intType => inferrer.intType; | |
| 34 TypeMask get stringType => inferrer.stringType; | |
| 35 TypeMask get listType => inferrer.listType; | |
| 36 TypeMask get mapType => inferrer.mapType; | |
| 37 | |
| 38 // TODO(karlklose): the map should be per continuation. | |
| 39 Map<Node, TypeMask> map = <Node, TypeMask>{}; | |
| 40 | |
| 41 TypeMaskSystem(dart2js.Compiler compiler) | |
| 42 : inferrer = compiler.typesTask, | |
| 43 classWorld = compiler.world; | |
| 44 | |
| 45 TypeMask getType(Node node) => map[node]; | |
| 46 | |
| 47 setType(Primitive node, TypeMask type) => map[node] = type; | |
| 48 | |
| 49 TypeMask getParameterType(ParameterElement parameter) { | |
| 50 return inferrer.getGuaranteedTypeOfElement(parameter); | |
| 51 } | |
| 52 | |
| 53 TypeMask getReturnType(FunctionElement function) { | |
| 54 return inferrer.getGuaranteedReturnTypeOfElement(function); | |
| 55 } | |
| 56 | |
| 57 @override | |
| 58 bool areEqual(TypeMask a, TypeMask b) { | |
| 59 return identical(a, b) || | |
| 60 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
| |
| 61 a.containsMask(b, classWorld); | |
| 62 } | |
| 63 | |
| 64 @override | |
| 65 bool areAssignable(TypeMask a, TypeMask b) { | |
| 66 return a.containsMask(b, classWorld) || b.containsMask(a, classWorld); | |
| 67 } | |
| 68 | |
| 69 @override | |
| 70 TypeMask join(TypeMask a, TypeMask b) { | |
| 71 return a.union(b, classWorld); | |
| 72 } | |
| 73 | |
| 74 @override | |
| 75 TypeMask typeOf(ConstantValue constant) { | |
| 76 return constant.computeMask(inferrer.compiler); | |
| 77 } | |
| 78 } | |
| 79 | |
| 80 typedef void InternalErrorFunction(Spannable location, String message); | |
| 81 | |
| 7 /** | 82 /** |
| 8 * Propagates constants throughout the IR, and replaces branches with fixed | 83 * Propagates types (including value types for constants) throughout the IR, and |
| 9 * jumps as well as side-effect free expressions with known constant results. | 84 * replaces branches with fixed jumps as well as side-effect free expressions |
| 85 * with known constant results. | |
| 86 * | |
| 10 * Should be followed by the [ShrinkingReducer] pass. | 87 * Should be followed by the [ShrinkingReducer] pass. |
| 11 * | 88 * |
| 12 * Implemented according to 'Constant Propagation with Conditional Branches' | 89 * Implemented according to 'Constant Propagation with Conditional Branches' |
| 13 * by Wegman, Zadeck. | 90 * by Wegman, Zadeck. |
| 14 */ | 91 */ |
| 15 class ConstantPropagator extends Pass { | 92 class TypePropagator<T> extends Pass { |
| 16 | 93 // TODO(karlklose): remove reference to _compiler. It is currently used to |
| 17 // Required for type determination in analysis of TypeOperator expressions. | 94 // compute [TypeMask]s. |
| 18 final dart2js.Compiler _compiler; | 95 final dart2js.Compiler _compiler; |
| 19 | 96 |
| 20 // The constant system is used for evaluation of expressions with constant | 97 // The constant system is used for evaluation of expressions with constant |
| 21 // arguments. | 98 // arguments. |
| 22 final dart2js.ConstantSystem _constantSystem; | 99 final dart2js.ConstantSystem _constantSystem; |
| 100 final TypeSystem _typeSystem; | |
| 101 final InternalErrorFunction _internalError; | |
| 102 final Map<Node, _AbstractValue> _types; | |
| 23 | 103 |
| 24 ConstantPropagator(this._compiler, this._constantSystem); | 104 |
| 105 TypePropagator(this._compiler, | |
| 106 this._constantSystem, | |
| 107 this._typeSystem, | |
| 108 this._internalError) | |
| 109 : _types = <Node, _AbstractValue>{}; | |
| 25 | 110 |
| 26 void _rewriteExecutableDefinition(ExecutableDefinition root) { | 111 void _rewriteExecutableDefinition(ExecutableDefinition root) { |
| 27 // Set all parent pointers. | 112 // Set all parent pointers. |
| 28 new ParentVisitor().visit(root); | 113 new ParentVisitor().visit(root); |
| 29 | 114 |
| 30 // Analyze. In this phase, the entire term is analyzed for reachability | 115 // Analyze. In this phase, the entire term is analyzed for reachability |
| 31 // and the constant status of each expression. | 116 // 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.
| |
| 117 _ConstPropagationVisitor<T> analyzer = new _ConstPropagationVisitor<T>( | |
| 118 _constantSystem, | |
| 119 _typeSystem, | |
| 120 _types, | |
| 121 _internalError, | |
| 122 _compiler); | |
| 32 | 123 |
| 33 _ConstPropagationVisitor analyzer = | |
| 34 new _ConstPropagationVisitor(_compiler, _constantSystem); | |
| 35 analyzer.analyze(root); | 124 analyzer.analyze(root); |
| 36 | 125 |
| 37 // Transform. Uses the data acquired in the previous analysis phase to | 126 // Transform. Uses the data acquired in the previous analysis phase to |
| 38 // replace branches with fixed targets and side-effect-free expressions | 127 // replace branches with fixed targets and side-effect-free expressions |
| 39 // with constant results. | 128 // with constant results. |
| 40 | |
| 41 _TransformingVisitor transformer = new _TransformingVisitor( | 129 _TransformingVisitor transformer = new _TransformingVisitor( |
| 42 analyzer.reachableNodes, analyzer.node2value); | 130 analyzer.reachableNodes, analyzer.values, _internalError); |
| 43 transformer.transform(root); | 131 transformer.transform(root); |
| 44 } | 132 } |
| 45 | 133 |
| 46 void rewriteFunctionDefinition(FunctionDefinition root) { | 134 void rewriteFunctionDefinition(FunctionDefinition root) { |
| 47 if (root.isAbstract) return; | 135 if (root.isAbstract) return; |
| 48 _rewriteExecutableDefinition(root); | 136 _rewriteExecutableDefinition(root); |
| 49 } | 137 } |
| 50 | 138 |
| 51 void rewriteFieldDefinition(FieldDefinition root) { | 139 void rewriteFieldDefinition(FieldDefinition root) { |
| 52 if (!root.hasInitializer) return; | 140 if (!root.hasInitializer) return; |
| 53 _rewriteExecutableDefinition(root); | 141 _rewriteExecutableDefinition(root); |
| 54 } | 142 } |
| 55 | 143 |
| 144 getType(Node node) => _types[node]; | |
| 56 } | 145 } |
| 57 | 146 |
| 58 /** | 147 /** |
| 59 * Uses the information from a preceding analysis pass in order to perform the | 148 * Uses the information from a preceding analysis pass in order to perform the |
| 60 * actual transformations on the CPS graph. | 149 * actual transformations on the CPS graph. |
| 61 */ | 150 */ |
| 62 class _TransformingVisitor extends RecursiveVisitor { | 151 class _TransformingVisitor extends RecursiveVisitor { |
| 152 final Set<Node> reachable; | |
| 153 final Map<Node, _AbstractValue> values; | |
| 63 | 154 |
| 64 final Set<Node> reachable; | 155 final InternalErrorFunction internalError; |
| 65 final Map<Node, _ConstnessLattice> node2value; | |
| 66 | 156 |
| 67 _TransformingVisitor(this.reachable, this.node2value); | 157 _TransformingVisitor(this.reachable, this.values, this.internalError); |
| 68 | 158 |
| 69 void transform(ExecutableDefinition root) { | 159 void transform(ExecutableDefinition root) { |
| 70 visit(root); | 160 visit(root); |
| 71 } | 161 } |
| 72 | 162 |
| 73 /// Given an expression with a known constant result and a continuation, | 163 /// Given an expression with a known constant result and a continuation, |
| 74 /// replaces the expression by a new LetPrim / InvokeContinuation construct. | 164 /// replaces the expression by a new LetPrim / InvokeContinuation construct. |
| 75 /// `unlink` is a closure responsible for unlinking all removed references. | 165 /// `unlink` is a closure responsible for unlinking all removed references. |
| 76 LetPrim constifyExpression(Expression node, | 166 LetPrim constifyExpression(Expression node, |
| 77 Continuation continuation, | 167 Continuation continuation, |
| 78 void unlink()) { | 168 void unlink()) { |
| 79 _ConstnessLattice cell = node2value[node]; | 169 _AbstractValue value = values[node]; |
| 80 if (cell == null || !cell.isConstant) { | 170 if (value == null || !value.isConstant) { |
| 81 return null; | 171 return null; |
| 82 } | 172 } |
| 83 | 173 |
| 84 assert(continuation.parameters.length == 1); | 174 assert(continuation.parameters.length == 1); |
| 85 | 175 |
| 86 // Set up the replacement structure. | 176 // Set up the replacement structure. |
| 87 | 177 PrimitiveConstantValue primitiveConstant = value.constant; |
| 88 PrimitiveConstantValue primitiveConstant = cell.constant; | |
| 89 ConstantExpression constExp = | 178 ConstantExpression constExp = |
| 90 new PrimitiveConstantExpression(primitiveConstant); | 179 new PrimitiveConstantExpression(primitiveConstant); |
| 91 Constant constant = new Constant(constExp); | 180 Constant constant = new Constant(constExp); |
| 92 LetPrim letPrim = new LetPrim(constant); | 181 LetPrim letPrim = new LetPrim(constant); |
| 93 InvokeContinuation invoke = | 182 InvokeContinuation invoke = |
| 94 new InvokeContinuation(continuation, <Primitive>[constant]); | 183 new InvokeContinuation(continuation, <Primitive>[constant]); |
| 95 | 184 |
| 96 invoke.parent = constant.parent = letPrim; | 185 invoke.parent = constant.parent = letPrim; |
| 97 letPrim.body = invoke; | 186 letPrim.body = invoke; |
| 98 | 187 |
| (...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 200 visitLetPrim(letPrim); | 289 visitLetPrim(letPrim); |
| 201 } | 290 } |
| 202 } | 291 } |
| 203 } | 292 } |
| 204 | 293 |
| 205 /** | 294 /** |
| 206 * Runs an analysis pass on the given function definition in order to detect | 295 * Runs an analysis pass on the given function definition in order to detect |
| 207 * const-ness as well as reachability, both of which are used in the subsequent | 296 * const-ness as well as reachability, both of which are used in the subsequent |
| 208 * transformation pass. | 297 * transformation pass. |
| 209 */ | 298 */ |
| 210 class _ConstPropagationVisitor extends Visitor { | 299 class _ConstPropagationVisitor<T> extends Visitor { |
| 211 // The node worklist stores nodes that are both reachable and need to be | 300 // The node worklist stores nodes that are both reachable and need to be |
| 212 // processed, but have not been processed yet. Using a worklist avoids deep | 301 // processed, but have not been processed yet. Using a worklist avoids deep |
| 213 // recursion. | 302 // recursion. |
| 214 // The node worklist and the reachable set operate in concert: nodes are | 303 // The node worklist and the reachable set operate in concert: nodes are |
| 215 // only ever added to the worklist when they have not yet been marked as | 304 // only ever added to the worklist when they have not yet been marked as |
| 216 // reachable, and adding a node to the worklist is always followed by marking | 305 // reachable, and adding a node to the worklist is always followed by marking |
| 217 // it reachable. | 306 // it reachable. |
| 218 // TODO(jgruber): Storing reachability per-edge instead of per-node would | 307 // TODO(jgruber): Storing reachability per-edge instead of per-node would |
| 219 // allow for further optimizations. | 308 // allow for further optimizations. |
| 220 final List<Node> nodeWorklist = <Node>[]; | 309 final List<Node> nodeWorklist = <Node>[]; |
| 221 final Set<Node> reachableNodes = new Set<Node>(); | 310 final Set<Node> reachableNodes = new Set<Node>(); |
| 222 | 311 |
| 223 // The definition workset stores all definitions which need to be reprocessed | 312 // The definition workset stores all definitions which need to be reprocessed |
| 224 // since their lattice value has changed. | 313 // since their lattice value has changed. |
| 225 final Set<Definition> defWorkset = new Set<Definition>(); | 314 final Set<Definition> defWorkset = new Set<Definition>(); |
| 226 | 315 |
| 227 final dart2js.Compiler compiler; | |
| 228 final dart2js.ConstantSystem constantSystem; | 316 final dart2js.ConstantSystem constantSystem; |
| 317 final TypeSystem typeSystem; | |
| 318 final InternalErrorFunction internalError; | |
| 319 final Compiler compiler; | |
| 320 | |
| 321 _AbstractValue unknownDynamic; | |
| 322 | |
| 323 _AbstractValue unknown([T t]) { | |
| 324 if (t == null) { | |
| 325 return unknownDynamic; | |
| 326 } else { | |
| 327 return new _AbstractValue.unknown(t); | |
| 328 } | |
| 329 } | |
| 330 | |
| 331 _AbstractValue nonConst([T type]) { | |
| 332 if (type == null) { | |
| 333 type = typeSystem.dynamicType; | |
| 334 } | |
| 335 return new _AbstractValue.nonConst(type); | |
| 336 } | |
| 337 | |
| 338 _AbstractValue constantValue(ConstantValue constant, T type) { | |
| 339 return new _AbstractValue(constant, type); | |
| 340 } | |
| 229 | 341 |
| 230 // Stores the current lattice value for nodes. Note that it contains not only | 342 // Stores the current lattice value for nodes. Note that it contains not only |
| 231 // definitions as keys, but also expressions such as method invokes. | 343 // definitions as keys, but also expressions such as method invokes. |
| 232 // Access through [getValue] and [setValue]. | 344 // Access through [getValue] and [setValue]. |
| 233 final Map<Node, _ConstnessLattice> node2value = <Node, _ConstnessLattice>{}; | 345 final Map<Node, _AbstractValue> values; |
| 234 | 346 |
| 235 _ConstPropagationVisitor(this.compiler, this.constantSystem); | 347 _ConstPropagationVisitor(this.constantSystem, TypeSystem typeSystem, |
| 348 this.values, | |
| 349 this.internalError, this.compiler) | |
| 350 : this.unknownDynamic = new _AbstractValue.unknown(typeSystem.dynamicType), | |
| 351 this.typeSystem = typeSystem; | |
| 236 | 352 |
| 237 void analyze(ExecutableDefinition root) { | 353 void analyze(ExecutableDefinition root) { |
| 238 reachableNodes.clear(); | 354 reachableNodes.clear(); |
| 239 defWorkset.clear(); | 355 defWorkset.clear(); |
| 240 nodeWorklist.clear(); | 356 nodeWorklist.clear(); |
| 241 | 357 |
| 242 // Initially, only the root node is reachable. | 358 // Initially, only the root node is reachable. |
| 243 setReachable(root); | 359 setReachable(root); |
| 244 | 360 |
| 245 while (true) { | 361 while (true) { |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 269 void setReachable(Node node) { | 385 void setReachable(Node node) { |
| 270 if (!reachableNodes.contains(node)) { | 386 if (!reachableNodes.contains(node)) { |
| 271 reachableNodes.add(node); | 387 reachableNodes.add(node); |
| 272 nodeWorklist.add(node); | 388 nodeWorklist.add(node); |
| 273 } | 389 } |
| 274 } | 390 } |
| 275 | 391 |
| 276 /// Returns the lattice value corresponding to [node], defaulting to unknown. | 392 /// Returns the lattice value corresponding to [node], defaulting to unknown. |
| 277 /// | 393 /// |
| 278 /// Never returns null. | 394 /// Never returns null. |
| 279 _ConstnessLattice getValue(Node node) { | 395 _AbstractValue getValue(Node node) { |
| 280 _ConstnessLattice value = node2value[node]; | 396 _AbstractValue value = values[node]; |
| 281 return (value == null) ? _ConstnessLattice.Unknown : value; | 397 return (value == null) ? unknown() : value; |
| 282 } | 398 } |
| 283 | 399 |
| 284 /// Joins the passed lattice [updateValue] to the current value of [node], | 400 /// Joins the passed lattice [updateValue] to the current value of [node], |
| 285 /// and adds it to the definition work set if it has changed and [node] is | 401 /// and adds it to the definition work set if it has changed and [node] is |
| 286 /// a definition. | 402 /// a definition. |
| 287 void setValue(Node node, _ConstnessLattice updateValue) { | 403 void setValue(Node node, _AbstractValue updateValue) { |
| 288 _ConstnessLattice oldValue = getValue(node); | 404 _AbstractValue oldValue = getValue(node); |
| 289 _ConstnessLattice newValue = updateValue.join(oldValue); | 405 _AbstractValue newValue = updateValue.join(oldValue, typeSystem); |
| 290 if (oldValue == newValue) { | 406 if (oldValue == newValue) { |
| 291 return; | 407 return; |
| 292 } | 408 } |
| 293 | 409 |
| 294 // Values may only move in the direction UNKNOWN -> CONSTANT -> NONCONST. | 410 // Values may only move in the direction UNKNOWN -> CONSTANT -> NONCONST. |
| 295 assert(newValue.kind >= oldValue.kind); | 411 assert(newValue.kind >= oldValue.kind); |
| 296 | 412 |
| 297 node2value[node] = newValue; | 413 values[node] = newValue; |
| 298 if (node is Definition) { | 414 if (node is Definition) { |
| 299 defWorkset.add(node); | 415 defWorkset.add(node); |
| 300 } | 416 } |
| 301 } | 417 } |
| 302 | 418 |
| 303 // -------------------------- Visitor overrides ------------------------------ | 419 // -------------------------- Visitor overrides ------------------------------ |
| 304 | 420 |
| 305 void visitNode(Node node) { | 421 void visitNode(Node node) { |
| 306 compiler.internalError(NO_LOCATION_SPANNABLE, | 422 internalError(NO_LOCATION_SPANNABLE, |
| 307 "_ConstPropagationVisitor is stale, add missing visit overrides"); | 423 "_ConstPropagationVisitor is stale, add missing visit overrides"); |
| 308 } | 424 } |
| 309 | 425 |
| 310 void visitFunctionDefinition(FunctionDefinition node) { | 426 void visitFunctionDefinition(FunctionDefinition node) { |
| 311 node.parameters.forEach(visit); | 427 node.parameters.forEach(visit); |
| 312 setReachable(node.body); | 428 setReachable(node.body); |
| 313 } | 429 } |
| 314 | 430 |
| 315 void visitFieldDefinition(FieldDefinition node) { | 431 void visitFieldDefinition(FieldDefinition node) { |
| 316 if (node.hasInitializer) { | 432 if (node.hasInitializer) { |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 329 // The continuation is only marked as reachable on use. | 445 // The continuation is only marked as reachable on use. |
| 330 setReachable(node.body); | 446 setReachable(node.body); |
| 331 } | 447 } |
| 332 | 448 |
| 333 void visitInvokeStatic(InvokeStatic node) { | 449 void visitInvokeStatic(InvokeStatic node) { |
| 334 Continuation cont = node.continuation.definition; | 450 Continuation cont = node.continuation.definition; |
| 335 setReachable(cont); | 451 setReachable(cont); |
| 336 | 452 |
| 337 assert(cont.parameters.length == 1); | 453 assert(cont.parameters.length == 1); |
| 338 Parameter returnValue = cont.parameters[0]; | 454 Parameter returnValue = cont.parameters[0]; |
| 339 setValue(returnValue, _ConstnessLattice.NonConst); | 455 Entity target = node.target; |
| 456 T returnType = target is FieldElement | |
| 457 ? typeSystem.dynamicType | |
| 458 : typeSystem.getReturnType(node.target); | |
| 459 setValue(returnValue, nonConst(returnType)); | |
| 340 } | 460 } |
| 341 | 461 |
| 342 void visitInvokeContinuation(InvokeContinuation node) { | 462 void visitInvokeContinuation(InvokeContinuation node) { |
| 343 Continuation cont = node.continuation.definition; | 463 Continuation cont = node.continuation.definition; |
| 344 setReachable(cont); | 464 setReachable(cont); |
| 345 | 465 |
| 346 // Forward the constant status of all continuation invokes to the | 466 // Forward the constant status of all continuation invokes to the |
| 347 // continuation. Note that this is effectively a phi node in SSA terms. | 467 // continuation. Note that this is effectively a phi node in SSA terms. |
| 348 for (int i = 0; i < node.arguments.length; i++) { | 468 for (int i = 0; i < node.arguments.length; i++) { |
| 349 Definition def = node.arguments[i].definition; | 469 Definition def = node.arguments[i].definition; |
| 350 _ConstnessLattice cell = getValue(def); | 470 _AbstractValue cell = getValue(def); |
| 351 setValue(cont.parameters[i], cell); | 471 setValue(cont.parameters[i], cell); |
| 352 } | 472 } |
| 353 } | 473 } |
| 354 | 474 |
| 355 void visitInvokeMethod(InvokeMethod node) { | 475 void visitInvokeMethod(InvokeMethod node) { |
| 356 Continuation cont = node.continuation.definition; | 476 Continuation cont = node.continuation.definition; |
| 357 setReachable(cont); | 477 setReachable(cont); |
| 358 | 478 |
| 359 /// Sets the value of both the current node and the target continuation | 479 /// Sets the value of both the current node and the target continuation |
| 360 /// parameter. | 480 /// parameter. |
| 361 void setValues(_ConstnessLattice updateValue) { | 481 void setValues(_AbstractValue updateValue) { |
| 362 setValue(node, updateValue); | 482 setValue(node, updateValue); |
| 363 Parameter returnValue = cont.parameters[0]; | 483 Parameter returnValue = cont.parameters[0]; |
| 364 setValue(returnValue, updateValue); | 484 setValue(returnValue, updateValue); |
| 365 } | 485 } |
| 366 | 486 |
| 367 _ConstnessLattice lhs = getValue(node.receiver.definition); | 487 _AbstractValue lhs = getValue(node.receiver.definition); |
| 368 if (lhs.isUnknown) { | 488 if (lhs.isUnknown) { |
| 369 // This may seem like a missed opportunity for evaluating short-circuiting | 489 // This may seem like a missed opportunity for evaluating short-circuiting |
| 370 // boolean operations; we are currently skipping these intentionally since | 490 // boolean operations; we are currently skipping these intentionally since |
| 371 // expressions such as `(new Foo() || true)` may introduce type errors | 491 // expressions such as `(new Foo() || true)` may introduce type errors |
| 372 // and thus evaluation to `true` would not be correct. | 492 // and thus evaluation to `true` would not be correct. |
| 373 // TODO(jgruber): Handle such cases while ensuring that new Foo() and | 493 // TODO(jgruber): Handle such cases while ensuring that new Foo() and |
| 374 // a type-check (in checked mode) are still executed. | 494 // a type-check (in checked mode) are still executed. |
| 375 return; // And come back later. | 495 return; // And come back later. |
| 376 } else if (lhs.isNonConst) { | 496 } else if (lhs.isNonConst) { |
| 377 setValues(_ConstnessLattice.NonConst); | 497 setValues(nonConst()); |
| 378 return; | 498 return; |
| 379 } else if (!node.selector.isOperator) { | 499 } else if (!node.selector.isOperator) { |
| 380 // TODO(jgruber): Handle known methods on constants such as String.length. | 500 // TODO(jgruber): Handle known methods on constants such as String.length. |
| 381 setValues(_ConstnessLattice.NonConst); | 501 setValues(nonConst()); |
| 382 return; | 502 return; |
| 383 } | 503 } |
| 384 | 504 |
| 385 // Calculate the resulting constant if possible. | 505 // Calculate the resulting constant if possible. |
| 386 ConstantValue result; | 506 ConstantValue result; |
| 387 String opname = node.selector.name; | 507 String opname = node.selector.name; |
| 388 if (node.selector.argumentCount == 0) { | 508 if (node.selector.argumentCount == 0) { |
| 389 // Unary operator. | 509 // Unary operator. |
| 390 | 510 |
| 391 if (opname == "unary-") { | 511 if (opname == "unary-") { |
| 392 opname = "-"; | 512 opname = "-"; |
| 393 } | 513 } |
| 394 dart2js.UnaryOperation operation = constantSystem.lookupUnary(opname); | 514 dart2js.UnaryOperation operation = constantSystem.lookupUnary(opname); |
| 395 if (operation != null) { | 515 if (operation != null) { |
| 396 result = operation.fold(lhs.constant); | 516 result = operation.fold(lhs.constant); |
| 397 } | 517 } |
| 398 } else if (node.selector.argumentCount == 1) { | 518 } else if (node.selector.argumentCount == 1) { |
| 399 // Binary operator. | 519 // Binary operator. |
| 400 | 520 |
| 401 _ConstnessLattice rhs = getValue(node.arguments[0].definition); | 521 _AbstractValue rhs = getValue(node.arguments[0].definition); |
| 402 if (!rhs.isConstant) { | 522 if (!rhs.isConstant) { |
| 403 setValues(rhs); | 523 setValues(rhs); |
| 404 return; | 524 return; |
| 405 } | 525 } |
| 406 | 526 |
| 407 dart2js.BinaryOperation operation = constantSystem.lookupBinary(opname); | 527 dart2js.BinaryOperation operation = constantSystem.lookupBinary(opname); |
| 408 if (operation != null) { | 528 if (operation != null) { |
| 409 result = operation.fold(lhs.constant, rhs.constant); | 529 result = operation.fold(lhs.constant, rhs.constant); |
| 410 } | 530 } |
| 411 } | 531 } |
| 412 | 532 |
| 413 // Update value of the continuation parameter. Again, this is effectively | 533 // Update value of the continuation parameter. Again, this is effectively |
| 414 // a phi. | 534 // a phi. |
| 415 | 535 if (result == null) { |
| 416 setValues((result == null) ? | 536 setValues(nonConst()); |
| 417 _ConstnessLattice.NonConst : new _ConstnessLattice(result)); | 537 } else { |
| 538 T type = typeSystem.typeOf(result); | |
| 539 setValues(new _AbstractValue(result, type)); | |
| 540 } | |
| 418 } | 541 } |
| 419 | 542 |
| 420 void visitInvokeSuperMethod(InvokeSuperMethod node) { | 543 void visitInvokeSuperMethod(InvokeSuperMethod node) { |
| 421 Continuation cont = node.continuation.definition; | 544 Continuation cont = node.continuation.definition; |
| 422 setReachable(cont); | 545 setReachable(cont); |
| 423 | 546 |
| 424 assert(cont.parameters.length == 1); | 547 assert(cont.parameters.length == 1); |
| 425 Parameter returnValue = cont.parameters[0]; | 548 Parameter returnValue = cont.parameters[0]; |
| 426 setValue(returnValue, _ConstnessLattice.NonConst); | 549 // TODO(karlklose): lookup the function and get ites return type. |
| 550 setValue(returnValue, nonConst()); | |
| 427 } | 551 } |
| 428 | 552 |
| 429 void visitInvokeConstructor(InvokeConstructor node) { | 553 void visitInvokeConstructor(InvokeConstructor node) { |
| 430 Continuation cont = node.continuation.definition; | 554 Continuation cont = node.continuation.definition; |
| 431 setReachable(cont); | 555 setReachable(cont); |
| 432 | 556 |
| 433 assert(cont.parameters.length == 1); | 557 assert(cont.parameters.length == 1); |
| 434 Parameter returnValue = cont.parameters[0]; | 558 Parameter returnValue = cont.parameters[0]; |
| 435 setValue(returnValue, _ConstnessLattice.NonConst); | 559 setValue(returnValue, nonConst()); |
| 436 } | 560 } |
| 437 | 561 |
| 438 void visitConcatenateStrings(ConcatenateStrings node) { | 562 void visitConcatenateStrings(ConcatenateStrings node) { |
| 439 Continuation cont = node.continuation.definition; | 563 Continuation cont = node.continuation.definition; |
| 440 setReachable(cont); | 564 setReachable(cont); |
| 441 | 565 |
| 442 void setValues(_ConstnessLattice updateValue) { | 566 void setValues(_AbstractValue updateValue) { |
| 443 setValue(node, updateValue); | 567 setValue(node, updateValue); |
| 444 Parameter returnValue = cont.parameters[0]; | 568 Parameter returnValue = cont.parameters[0]; |
| 445 setValue(returnValue, updateValue); | 569 setValue(returnValue, updateValue); |
| 446 } | 570 } |
| 447 | 571 |
| 448 // TODO(jgruber): Currently we only optimize if all arguments are string | 572 // TODO(jgruber): Currently we only optimize if all arguments are string |
| 449 // constants, but we could also handle cases such as "foo${42}". | 573 // constants, but we could also handle cases such as "foo${42}". |
| 450 bool allStringConstants = node.arguments.every((Reference ref) { | 574 bool allStringConstants = node.arguments.every((Reference ref) { |
| 451 if (!(ref.definition is Constant)) { | 575 if (!(ref.definition is Constant)) { |
| 452 return false; | 576 return false; |
| 453 } | 577 } |
| 454 Constant constant = ref.definition; | 578 Constant constant = ref.definition; |
| 455 return constant != null && constant.value.isString; | 579 return constant != null && constant.value.isString; |
| 456 }); | 580 }); |
| 457 | 581 |
| 582 T type = typeSystem.stringType; | |
| 458 assert(cont.parameters.length == 1); | 583 assert(cont.parameters.length == 1); |
| 459 if (allStringConstants) { | 584 if (allStringConstants) { |
| 460 // All constant, we can concatenate ourselves. | 585 // All constant, we can concatenate ourselves. |
| 461 Iterable<String> allStrings = node.arguments.map((Reference ref) { | 586 Iterable<String> allStrings = node.arguments.map((Reference ref) { |
| 462 Constant constant = ref.definition; | 587 Constant constant = ref.definition; |
| 463 StringConstantValue stringConstant = constant.value; | 588 StringConstantValue stringConstant = constant.value; |
| 464 return stringConstant.primitiveValue.slowToString(); | 589 return stringConstant.primitiveValue.slowToString(); |
| 465 }); | 590 }); |
| 466 LiteralDartString dartString = new LiteralDartString(allStrings.join()); | 591 LiteralDartString dartString = new LiteralDartString(allStrings.join()); |
| 467 ConstantValue constant = new StringConstantValue(dartString); | 592 ConstantValue constant = new StringConstantValue(dartString); |
| 468 setValues(new _ConstnessLattice(constant)); | 593 setValues(new _AbstractValue(constant, type)); |
| 469 } else { | 594 } else { |
| 470 setValues(_ConstnessLattice.NonConst); | 595 setValues(nonConst(type)); |
| 471 } | 596 } |
| 472 } | 597 } |
| 473 | 598 |
| 474 void visitBranch(Branch node) { | 599 void visitBranch(Branch node) { |
| 475 IsTrue isTrue = node.condition; | 600 IsTrue isTrue = node.condition; |
| 476 _ConstnessLattice conditionCell = getValue(isTrue.value.definition); | 601 _AbstractValue conditionCell = getValue(isTrue.value.definition); |
| 477 | 602 |
| 478 if (conditionCell.isUnknown) { | 603 if (conditionCell.isUnknown) { |
| 479 return; // And come back later. | 604 return; // And come back later. |
| 480 } else if (conditionCell.isNonConst) { | 605 } else if (conditionCell.isNonConst) { |
| 481 setReachable(node.trueContinuation.definition); | 606 setReachable(node.trueContinuation.definition); |
| 482 setReachable(node.falseContinuation.definition); | 607 setReachable(node.falseContinuation.definition); |
| 483 } else if (conditionCell.isConstant && | 608 } else if (conditionCell.isConstant && |
| 484 !(conditionCell.constant.isBool)) { | 609 !(conditionCell.constant.isBool)) { |
| 485 // Treat non-bool constants in condition as non-const since they result | 610 // Treat non-bool constants in condition as non-const since they result |
| 486 // in type errors in checked mode. | 611 // in type errors in checked mode. |
| 487 // TODO(jgruber): Default to false in unchecked mode. | 612 // TODO(jgruber): Default to false in unchecked mode. |
| 488 setReachable(node.trueContinuation.definition); | 613 setReachable(node.trueContinuation.definition); |
| 489 setReachable(node.falseContinuation.definition); | 614 setReachable(node.falseContinuation.definition); |
| 490 setValue(isTrue.value.definition, _ConstnessLattice.NonConst); | 615 setValue(isTrue.value.definition, nonConst(typeSystem.boolType)); |
| 491 } else if (conditionCell.isConstant && | 616 } else if (conditionCell.isConstant && |
| 492 conditionCell.constant.isBool) { | 617 conditionCell.constant.isBool) { |
| 493 BoolConstantValue boolConstant = conditionCell.constant; | 618 BoolConstantValue boolConstant = conditionCell.constant; |
| 494 setReachable((boolConstant.isTrue) ? | 619 setReachable((boolConstant.isTrue) ? |
| 495 node.trueContinuation.definition : node.falseContinuation.definition); | 620 node.trueContinuation.definition : node.falseContinuation.definition); |
| 496 } | 621 } |
| 497 } | 622 } |
| 498 | 623 |
| 499 void visitTypeOperator(TypeOperator node) { | 624 void visitTypeOperator(TypeOperator node) { |
| 500 Continuation cont = node.continuation.definition; | 625 Continuation cont = node.continuation.definition; |
| 501 setReachable(cont); | 626 setReachable(cont); |
| 502 | 627 |
| 503 void setValues(_ConstnessLattice updateValue) { | 628 void setValues(_AbstractValue updateValue) { |
| 504 setValue(node, updateValue); | 629 setValue(node, updateValue); |
| 505 Parameter returnValue = cont.parameters[0]; | 630 Parameter returnValue = cont.parameters[0]; |
| 506 setValue(returnValue, updateValue); | 631 setValue(returnValue, updateValue); |
| 507 } | 632 } |
| 508 | 633 |
| 509 if (node.isTypeCast) { | 634 if (node.isTypeCast) { |
| 510 // TODO(jgruber): Add support for `as` casts. | 635 // TODO(jgruber): Add support for `as` casts. |
| 511 setValues(_ConstnessLattice.NonConst); | 636 setValues(nonConst()); |
| 512 } | 637 } |
| 513 | 638 |
| 514 _ConstnessLattice cell = getValue(node.receiver.definition); | 639 _AbstractValue cell = getValue(node.receiver.definition); |
| 515 if (cell.isUnknown) { | 640 if (cell.isUnknown) { |
| 516 return; // And come back later. | 641 return; // And come back later. |
| 517 } else if (cell.isNonConst) { | 642 } else if (cell.isNonConst) { |
| 518 setValues(_ConstnessLattice.NonConst); | 643 setValues(nonConst(cell.type)); |
| 519 } else if (node.type.kind == types.TypeKind.INTERFACE) { | 644 } else if (node.type.kind == types.TypeKind.INTERFACE) { |
| 520 // Receiver is a constant, perform is-checks at compile-time. | 645 // Receiver is a constant, perform is-checks at compile-time. |
| 521 | 646 |
| 522 types.InterfaceType checkedType = node.type; | 647 types.InterfaceType checkedType = node.type; |
| 523 ConstantValue constant = cell.constant; | 648 ConstantValue constant = cell.constant; |
| 649 // TODO(karlklose): remove call to computeType. | |
| 524 types.DartType constantType = constant.computeType(compiler); | 650 types.DartType constantType = constant.computeType(compiler); |
| 525 | 651 |
| 526 _ConstnessLattice result = _ConstnessLattice.NonConst; | 652 T type = typeSystem.boolType; |
| 653 _AbstractValue result; | |
| 527 if (constant.isNull && | 654 if (constant.isNull && |
| 528 checkedType.element != compiler.nullClass && | 655 checkedType.element != compiler.nullClass && |
| 529 checkedType.element != compiler.objectClass) { | 656 checkedType.element != compiler.objectClass) { |
| 530 // `(null is Type)` is true iff Type is in { Null, Object }. | 657 // `(null is Type)` is true iff Type is in { Null, Object }. |
| 531 result = new _ConstnessLattice(new FalseConstantValue()); | 658 result = constantValue(new FalseConstantValue(), type); |
| 532 } else { | 659 } else { |
| 533 // Otherwise, perform a standard subtype check. | 660 // Otherwise, perform a standard subtype check. |
| 534 result = new _ConstnessLattice( | 661 result = constantValue( |
| 535 constantSystem.isSubtype(compiler, constantType, checkedType) | 662 constantSystem.isSubtype(compiler, constantType, checkedType) |
| 536 ? new TrueConstantValue() | 663 ? new TrueConstantValue() |
| 537 : new FalseConstantValue()); | 664 : new FalseConstantValue(), |
| 665 type); | |
| 538 } | 666 } |
| 539 | |
| 540 setValues(result); | 667 setValues(result); |
| 541 } | 668 } |
| 542 } | 669 } |
| 543 | 670 |
| 544 void visitSetClosureVariable(SetClosureVariable node) { | 671 void visitSetClosureVariable(SetClosureVariable node) { |
| 545 setReachable(node.body); | 672 setReachable(node.body); |
| 546 } | 673 } |
| 547 | 674 |
| 548 void visitDeclareFunction(DeclareFunction node) { | 675 void visitDeclareFunction(DeclareFunction node) { |
| 549 setReachable(node.definition); | 676 setReachable(node.definition); |
| 550 setReachable(node.body); | 677 setReachable(node.body); |
| 551 } | 678 } |
| 552 | 679 |
| 553 // Definitions. | 680 // Definitions. |
| 554 void visitLiteralList(LiteralList node) { | 681 void visitLiteralList(LiteralList node) { |
| 555 // Constant lists are translated into (Constant ListConstant(...)) IR nodes, | 682 // Constant lists are translated into (Constant ListConstant(...)) IR nodes, |
| 556 // and thus LiteralList nodes are NonConst. | 683 // and thus LiteralList nodes are NonConst. |
| 557 setValue(node, _ConstnessLattice.NonConst); | 684 setValue(node, nonConst(typeSystem.listType)); |
| 558 } | 685 } |
| 559 | 686 |
| 560 void visitLiteralMap(LiteralMap node) { | 687 void visitLiteralMap(LiteralMap node) { |
| 561 // Constant maps are translated into (Constant MapConstant(...)) IR nodes, | 688 // Constant maps are translated into (Constant MapConstant(...)) IR nodes, |
| 562 // and thus LiteralMap nodes are NonConst. | 689 // and thus LiteralMap nodes are NonConst. |
| 563 setValue(node, _ConstnessLattice.NonConst); | 690 setValue(node, nonConst(typeSystem.mapType)); |
| 564 } | 691 } |
| 565 | 692 |
| 566 void visitConstant(Constant node) { | 693 void visitConstant(Constant node) { |
| 567 setValue(node, new _ConstnessLattice(node.value)); | 694 ConstantValue value = node.value; |
| 695 setValue(node, constantValue(value, typeSystem.typeOf(value))); | |
| 568 } | 696 } |
| 569 | 697 |
| 570 void visitThis(This node) { | 698 void visitThis(This node) { |
| 571 setValue(node, _ConstnessLattice.NonConst); | 699 // TODO(karlklose): Add the type. |
| 700 setValue(node, nonConst()); | |
| 572 } | 701 } |
| 573 | 702 |
| 574 void visitReifyTypeVar(ReifyTypeVar node) { | 703 void visitReifyTypeVar(ReifyTypeVar node) { |
| 575 setValue(node, _ConstnessLattice.NonConst); | 704 setValue(node, nonConst(typeSystem.typeType)); |
| 576 } | 705 } |
| 577 | 706 |
| 578 void visitCreateFunction(CreateFunction node) { | 707 void visitCreateFunction(CreateFunction node) { |
| 579 setReachable(node.definition); | 708 setReachable(node.definition); |
| 580 ConstantValue constant = | 709 ConstantValue constant = |
| 581 new FunctionConstantValue(node.definition.element); | 710 new FunctionConstantValue(node.definition.element); |
| 582 setValue(node, new _ConstnessLattice(constant)); | 711 setValue(node, constantValue(constant, typeSystem.functionType)); |
| 583 } | 712 } |
| 584 | 713 |
| 585 void visitGetClosureVariable(GetClosureVariable node) { | 714 void visitGetClosureVariable(GetClosureVariable node) { |
| 586 setValue(node, _ConstnessLattice.NonConst); | 715 setValue(node, nonConst()); |
| 587 } | 716 } |
| 588 | 717 |
| 589 void visitClosureVariable(ClosureVariable node) { | 718 void visitClosureVariable(ClosureVariable node) { |
| 590 } | 719 } |
| 591 | 720 |
| 592 void visitParameter(Parameter node) { | 721 void visitParameter(Parameter node) { |
| 722 T type = typeSystem.getParameterType(node.hint); | |
| 593 if (node.parent is FunctionDefinition) { | 723 if (node.parent is FunctionDefinition) { |
| 594 // Functions may escape and thus their parameters must be initialized to | 724 // Functions may escape and thus their parameters must be initialized to |
| 595 // NonConst. | 725 // NonConst. |
| 596 setValue(node, _ConstnessLattice.NonConst); | 726 setValue(node, nonConst(type)); |
| 597 } else if (node.parent is Continuation) { | 727 } else if (node.parent is Continuation) { |
| 598 // Continuations on the other hand are local, and parameters are | 728 // Continuations on the other hand are local, and parameters are |
| 599 // initialized to Unknown. | 729 // initialized to Unknown. |
| 600 setValue(node, _ConstnessLattice.Unknown); | 730 setValue(node, unknown()); |
| 601 } else { | 731 } else { |
| 602 compiler.internalError(node.hint, "Unexpected parent of Parameter"); | 732 internalError(node.hint, "Unexpected parent of Parameter"); |
| 603 } | 733 } |
| 604 } | 734 } |
| 605 | 735 |
| 606 void visitContinuation(Continuation node) { | 736 void visitContinuation(Continuation node) { |
| 607 node.parameters.forEach((Parameter p) { | 737 node.parameters.forEach((Parameter p) { |
| 608 setValue(p, _ConstnessLattice.Unknown); | 738 // TODO(karlklose): join parameter types from use sites. |
| 739 setValue(p, unknown()); | |
| 609 defWorkset.add(p); | 740 defWorkset.add(p); |
| 610 }); | 741 }); |
| 611 | 742 |
| 612 if (node.body != null) { | 743 if (node.body != null) { |
| 613 setReachable(node.body); | 744 setReachable(node.body); |
| 614 } | 745 } |
| 615 } | 746 } |
| 616 | 747 |
| 617 // Conditions. | 748 // Conditions. |
| 618 | 749 |
| 619 void visitIsTrue(IsTrue node) { | 750 void visitIsTrue(IsTrue node) { |
| 620 Branch branch = node.parent; | 751 Branch branch = node.parent; |
| 621 visitBranch(branch); | 752 visitBranch(branch); |
| 622 } | 753 } |
| 623 | 754 |
| 624 // JavaScript specific nodes. | 755 // JavaScript specific nodes. |
| 625 | 756 |
| 626 void visitIdentical(Identical node) { | 757 void visitIdentical(Identical node) { |
| 627 _ConstnessLattice leftConst = getValue(node.left.definition); | 758 _AbstractValue leftConst = getValue(node.left.definition); |
| 628 _ConstnessLattice rightConst = getValue(node.left.definition); | 759 _AbstractValue rightConst = getValue(node.right.definition); |
| 629 ConstantValue leftValue = leftConst.constant; | 760 ConstantValue leftValue = leftConst.constant; |
| 630 ConstantValue rightValue = rightConst.constant; | 761 ConstantValue rightValue = rightConst.constant; |
| 631 if (leftConst.isUnknown || rightConst.isUnknown) { | 762 if (leftConst.isUnknown || rightConst.isUnknown) { |
| 632 // Come back later. | 763 // Come back later. |
| 633 return; | 764 return; |
| 634 } else if (!leftConst.isConstant || !rightConst.isConstant) { | 765 } else if (!leftConst.isConstant || !rightConst.isConstant) { |
| 635 setValue(node, _ConstnessLattice.NonConst); | 766 T leftType = leftConst.type; |
| 767 T rightType = rightConst.type; | |
| 768 if (!typeSystem.areAssignable(leftType, rightType)) { | |
| 769 setValue(node, | |
| 770 constantValue(new FalseConstantValue(), typeSystem.boolType)); | |
| 771 } else { | |
| 772 setValue(node, nonConst(typeSystem.boolType)); | |
| 773 } | |
| 636 } else if (leftValue.isPrimitive && rightValue.isPrimitive) { | 774 } else if (leftValue.isPrimitive && rightValue.isPrimitive) { |
| 637 assert(leftConst.isConstant && rightConst.isConstant); | 775 assert(leftConst.isConstant && rightConst.isConstant); |
| 638 PrimitiveConstantValue left = leftValue; | 776 PrimitiveConstantValue left = leftValue; |
| 639 PrimitiveConstantValue right = rightValue; | 777 PrimitiveConstantValue right = rightValue; |
| 640 ConstantValue result = | 778 ConstantValue result = |
| 641 new BoolConstantValue(left.primitiveValue == right.primitiveValue); | 779 new BoolConstantValue(left.primitiveValue == right.primitiveValue); |
| 642 setValue(node, new _ConstnessLattice(result)); | 780 setValue(node, new _AbstractValue(result, typeSystem.boolType)); |
| 643 } | 781 } |
| 644 } | 782 } |
| 645 } | 783 } |
| 646 | 784 |
| 647 /// Represents the constant-state of a variable at some point in the program. | 785 /// Represents the constant-state of a variable at some point in the program. |
| 648 /// UNKNOWN: may be some as yet undetermined constant. | 786 /// UNKNOWN: may be some as yet undetermined constant. |
| 649 /// CONSTANT: is a constant as stored in the local field. | 787 /// CONSTANT: is a constant as stored in the local field. |
| 650 /// NONCONST: not a constant. | 788 /// NONCONST: not a constant. |
| 651 class _ConstnessLattice { | 789 class _AbstractValue<T> { |
| 652 static const int UNKNOWN = 0; | 790 static const int UNKNOWN = 0; |
| 653 static const int CONSTANT = 1; | 791 static const int CONSTANT = 1; |
| 654 static const int NONCONST = 2; | 792 static const int NONCONST = 2; |
| 655 | 793 |
| 656 final int kind; | 794 final int kind; |
| 657 final ConstantValue constant; | 795 final ConstantValue constant; |
| 796 final T type; | |
| 658 | 797 |
| 659 static final _ConstnessLattice Unknown = | 798 _AbstractValue._internal(this.kind, this.constant, this.type) { |
| 660 new _ConstnessLattice._internal(UNKNOWN, null); | 799 assert(kind != CONSTANT || constant != null); |
| 661 static final _ConstnessLattice NonConst = | 800 assert(type != null); |
| 662 new _ConstnessLattice._internal(NONCONST, null); | 801 } |
| 663 | 802 |
| 664 _ConstnessLattice._internal(this.kind, this.constant); | 803 _AbstractValue(ConstantValue constant, T type) |
| 665 _ConstnessLattice(this.constant) : kind = CONSTANT { | 804 : this._internal(CONSTANT, constant, type); |
| 666 assert(this.constant != null); | 805 |
| 667 } | 806 _AbstractValue.unknown(T type) |
| 807 : this._internal(UNKNOWN, null, type); | |
| 808 | |
| 809 _AbstractValue.nonConst(T type) | |
| 810 : this._internal(NONCONST, null, type); | |
| 668 | 811 |
| 669 bool get isUnknown => (kind == UNKNOWN); | 812 bool get isUnknown => (kind == UNKNOWN); |
| 670 bool get isConstant => (kind == CONSTANT); | 813 bool get isConstant => (kind == CONSTANT); |
| 671 bool get isNonConst => (kind == NONCONST); | 814 bool get isNonConst => (kind == NONCONST); |
| 672 | 815 |
| 673 int get hashCode => kind | (constant.hashCode << 2); | 816 int get hashCode { |
| 674 bool operator==(_ConstnessLattice that) => | 817 return kind | (constant.hashCode * 5) | type.hashCode * 7; |
| 675 (that.kind == this.kind && that.constant == this.constant); | 818 } |
| 819 | |
| 820 bool operator ==(_AbstractValue that) { | |
| 821 return that.kind == this.kind && | |
| 822 that.constant == this.constant && | |
| 823 that.type == this.type; | |
| 824 } | |
| 676 | 825 |
| 677 String toString() { | 826 String toString() { |
| 678 switch (kind) { | 827 switch (kind) { |
| 679 case UNKNOWN: return "Unknown"; | 828 case UNKNOWN: return "Unknown"; |
| 680 case CONSTANT: return "Constant: $constant"; | 829 case CONSTANT: return "Constant: $constant: $type"; |
| 681 case NONCONST: return "Non-constant"; | 830 case NONCONST: return "Non-constant: $type"; |
| 682 default: assert(false); | 831 default: assert(false); |
| 683 } | 832 } |
| 684 return null; | 833 return null; |
| 685 } | 834 } |
| 686 | 835 |
| 687 /// Compute the join of two values in the lattice. | 836 /// Compute the join of two values in the lattice. |
| 688 _ConstnessLattice join(_ConstnessLattice that) { | 837 _AbstractValue join(_AbstractValue that, TypeSystem typeSystem) { |
| 838 bool isDynamic(T type) { | |
| 839 return typeSystem.areEqual(type, typeSystem.dynamicType); | |
| 840 } | |
| 841 | |
| 689 assert(that != null); | 842 assert(that != null); |
| 690 | 843 |
| 691 if (this.isNonConst || that.isUnknown) { | 844 if (isDynamic(this.type) && isDynamic(that.type)) { |
| 692 return this; | 845 if (this.isNonConst || that.isUnknown) { |
| 846 return this; | |
| 847 } | |
| 848 | |
| 849 if (this.isUnknown || that.isNonConst) { | |
| 850 return that; | |
| 851 } | |
| 852 | |
| 853 if (this.constant == that.constant) { | |
| 854 return this; | |
| 855 } | |
| 856 | |
| 857 return new _AbstractValue.nonConst(typeSystem.dynamicType); | |
| 693 } | 858 } |
| 694 | 859 |
| 695 if (this.isUnknown || that.isNonConst) { | 860 if (this.isUnknown) { |
| 696 return that; | 861 return that; |
| 862 } else if (that.isUnknown) { | |
| 863 return this; | |
| 864 } else if (this.isConstant && that.isConstant && | |
| 865 this.constant == that.constant) { | |
| 866 return this; | |
| 867 } else { | |
| 868 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.
| |
| 697 } | 869 } |
| 698 | |
| 699 if (this.constant == that.constant) { | |
| 700 return this; | |
| 701 } | |
| 702 | |
| 703 return NonConst; | |
| 704 } | 870 } |
| 705 } | 871 } |
| OLD | NEW |