| 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 import 'optimizers.dart' show Pass, ParentVisitor; | 5 import 'optimizers.dart' show Pass, ParentVisitor; |
| 6 | 6 |
| 7 import '../constants/constant_system.dart'; | 7 import '../constants/constant_system.dart'; |
| 8 import '../constants/expressions.dart'; | 8 import '../constants/expressions.dart'; |
| 9 import '../resolution/operators.dart'; | 9 import '../resolution/operators.dart'; |
| 10 import '../constants/values.dart'; | 10 import '../constants/values.dart'; |
| 11 import '../dart_types.dart' as types; | 11 import '../dart_types.dart' as types; |
| 12 import '../dart2jslib.dart' as dart2js; | 12 import '../dart2jslib.dart' as dart2js; |
| 13 import '../tree/tree.dart' show LiteralDartString; | 13 import '../tree/tree.dart' show LiteralDartString; |
| 14 import 'cps_ir_nodes.dart'; | 14 import 'cps_ir_nodes.dart'; |
| 15 import '../types/types.dart' show TypeMask, TypesTask; | 15 import '../types/types.dart' show TypeMask, TypesTask; |
| 16 import '../types/constants.dart' show computeTypeMask; | 16 import '../types/constants.dart' show computeTypeMask; |
| 17 import '../elements/elements.dart' show ClassElement, Element, Entity, | 17 import '../elements/elements.dart' show ClassElement, Element, Entity, |
| 18 FieldElement, FunctionElement, ParameterElement; | 18 FieldElement, FunctionElement, ParameterElement; |
| 19 import '../dart2jslib.dart' show ClassWorld; | 19 import '../dart2jslib.dart' show ClassWorld; |
| 20 import '../universe/universe.dart'; | 20 import '../universe/universe.dart'; |
| 21 | 21 |
| 22 abstract class TypeSystem<T> { | 22 abstract class TypeSystem<T> { |
| 23 T get dynamicType; | 23 T get dynamicType; |
| 24 T get typeType; | 24 T get typeType; |
| 25 T get functionType; | 25 T get functionType; |
| 26 T get boolType; | 26 T get boolType; |
| 27 T get intType; | 27 T get intType; |
| 28 T get numType; |
| 28 T get stringType; | 29 T get stringType; |
| 29 T get listType; | 30 T get listType; |
| 30 T get mapType; | 31 T get mapType; |
| 31 T get nonNullType; | 32 T get nonNullType; |
| 32 | 33 |
| 33 T getReturnType(FunctionElement element); | 34 T getReturnType(FunctionElement element); |
| 34 T getSelectorReturnType(Selector selector); | 35 T getSelectorReturnType(Selector selector); |
| 35 T getParameterType(ParameterElement element); | 36 T getParameterType(ParameterElement element); |
| 36 T getFieldType(FieldElement element); | 37 T getFieldType(FieldElement element); |
| 37 T join(T a, T b); | 38 T join(T a, T b); |
| 38 T exact(ClassElement element); | 39 T exact(ClassElement element); |
| 39 T getTypeOf(ConstantValue constant); | 40 T getTypeOf(ConstantValue constant); |
| 40 | 41 |
| 41 bool areDisjoint(T leftType, T rightType); | 42 bool areDisjoint(T leftType, T rightType); |
| 42 | 43 |
| 43 /// True if all values satisfying [type] are booleans (null is not a boolean). | 44 /// True if all values satisfying [type] are booleans (null is not a boolean). |
| 44 bool isDefinitelyBool(T type); | 45 bool isDefinitelyBool(T type); |
| 45 | 46 |
| 46 bool isDefinitelyNotNull(T type); | 47 bool isDefinitelyNotNull(T type); |
| 48 |
| 49 AbstractBool isSubtypeOf(T value, types.DartType type, {bool allowNull}); |
| 47 } | 50 } |
| 48 | 51 |
| 49 class UnitTypeSystem implements TypeSystem<String> { | 52 class UnitTypeSystem implements TypeSystem<String> { |
| 50 static const String UNIT = 'unit'; | 53 static const String UNIT = 'unit'; |
| 51 | 54 |
| 52 get boolType => UNIT; | 55 get boolType => UNIT; |
| 53 get dynamicType => UNIT; | 56 get dynamicType => UNIT; |
| 54 get functionType => UNIT; | 57 get functionType => UNIT; |
| 55 get intType => UNIT; | 58 get intType => UNIT; |
| 59 get numType => UNIT; |
| 56 get listType => UNIT; | 60 get listType => UNIT; |
| 57 get mapType => UNIT; | 61 get mapType => UNIT; |
| 58 get stringType => UNIT; | 62 get stringType => UNIT; |
| 59 get typeType => UNIT; | 63 get typeType => UNIT; |
| 60 get nonNullType => UNIT; | 64 get nonNullType => UNIT; |
| 61 | 65 |
| 62 getParameterType(_) => UNIT; | 66 getParameterType(_) => UNIT; |
| 63 getReturnType(_) => UNIT; | 67 getReturnType(_) => UNIT; |
| 64 getSelectorReturnType(_) => UNIT; | 68 getSelectorReturnType(_) => UNIT; |
| 65 getFieldType(_) => UNIT; | 69 getFieldType(_) => UNIT; |
| 66 join(a, b) => UNIT; | 70 join(a, b) => UNIT; |
| 67 exact(_) => UNIT; | 71 exact(_) => UNIT; |
| 68 getTypeOf(_) => UNIT; | 72 getTypeOf(_) => UNIT; |
| 69 | 73 |
| 74 bool areDisjoint(String leftType, String rightType) { |
| 75 return false; |
| 76 } |
| 77 |
| 70 bool isDefinitelyBool(_) => false; | 78 bool isDefinitelyBool(_) => false; |
| 71 | 79 |
| 72 bool isDefinitelyNotNull(_) => false; | 80 bool isDefinitelyNotNull(_) => false; |
| 73 | 81 |
| 74 bool areDisjoint(String leftType, String rightType) { | 82 AbstractBool isSubtypeOf(value, type, {bool allowNull}) { |
| 75 return false; | 83 return AbstractBool.Maybe; |
| 76 } | 84 } |
| 77 } | 85 } |
| 78 | 86 |
| 87 enum AbstractBool { |
| 88 True, False, Maybe, Nothing |
| 89 } |
| 90 |
| 79 class TypeMaskSystem implements TypeSystem<TypeMask> { | 91 class TypeMaskSystem implements TypeSystem<TypeMask> { |
| 80 final TypesTask inferrer; | 92 final TypesTask inferrer; |
| 81 final ClassWorld classWorld; | 93 final ClassWorld classWorld; |
| 82 | 94 |
| 83 TypeMask get dynamicType => inferrer.dynamicType; | 95 TypeMask get dynamicType => inferrer.dynamicType; |
| 84 TypeMask get typeType => inferrer.typeType; | 96 TypeMask get typeType => inferrer.typeType; |
| 85 TypeMask get functionType => inferrer.functionType; | 97 TypeMask get functionType => inferrer.functionType; |
| 86 TypeMask get boolType => inferrer.boolType; | 98 TypeMask get boolType => inferrer.boolType; |
| 87 TypeMask get intType => inferrer.intType; | 99 TypeMask get intType => inferrer.intType; |
| 100 TypeMask get numType => inferrer.numType; |
| 88 TypeMask get stringType => inferrer.stringType; | 101 TypeMask get stringType => inferrer.stringType; |
| 89 TypeMask get listType => inferrer.listType; | 102 TypeMask get listType => inferrer.listType; |
| 90 TypeMask get mapType => inferrer.mapType; | 103 TypeMask get mapType => inferrer.mapType; |
| 91 TypeMask get nonNullType => inferrer.nonNullType; | 104 TypeMask get nonNullType => inferrer.nonNullType; |
| 92 | 105 |
| 93 // TODO(karlklose): remove compiler here. | 106 // TODO(karlklose): remove compiler here. |
| 94 TypeMaskSystem(dart2js.Compiler compiler) | 107 TypeMaskSystem(dart2js.Compiler compiler) |
| 95 : inferrer = compiler.typesTask, | 108 : inferrer = compiler.typesTask, |
| 96 classWorld = compiler.world; | 109 classWorld = compiler.world; |
| 97 | 110 |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 133 return t.containsOnlyBool(classWorld) && !t.isNullable; | 146 return t.containsOnlyBool(classWorld) && !t.isNullable; |
| 134 } | 147 } |
| 135 | 148 |
| 136 bool isDefinitelyNotNull(TypeMask t) => !t.isNullable; | 149 bool isDefinitelyNotNull(TypeMask t) => !t.isNullable; |
| 137 | 150 |
| 138 @override | 151 @override |
| 139 bool areDisjoint(TypeMask leftType, TypeMask rightType) { | 152 bool areDisjoint(TypeMask leftType, TypeMask rightType) { |
| 140 TypeMask intersection = leftType.intersection(rightType, classWorld); | 153 TypeMask intersection = leftType.intersection(rightType, classWorld); |
| 141 return intersection.isEmpty && !intersection.isNullable; | 154 return intersection.isEmpty && !intersection.isNullable; |
| 142 } | 155 } |
| 156 |
| 157 AbstractBool isSubtypeOf(TypeMask value, |
| 158 types.DartType type, |
| 159 {bool allowNull}) { |
| 160 assert(allowNull != null); |
| 161 if (type is types.DynamicType) { |
| 162 if (!allowNull && value.isNullable) return AbstractBool.Maybe; |
| 163 return AbstractBool.True; |
| 164 } |
| 165 if (type is types.InterfaceType) { |
| 166 TypeMask typeAsMask = allowNull |
| 167 ? new TypeMask.subtype(type.element, classWorld) |
| 168 : new TypeMask.nonNullSubtype(type.element, classWorld); |
| 169 if (areDisjoint(value, typeAsMask)) { |
| 170 // Disprove the subtype relation based on the class alone. |
| 171 return AbstractBool.False; |
| 172 } |
| 173 if (!type.treatAsRaw) { |
| 174 // If there are type arguments, we cannot prove the subtype relation, |
| 175 // because the type arguments are unknown on both the value and type. |
| 176 return AbstractBool.Maybe; |
| 177 } |
| 178 if (typeAsMask.containsMask(value, classWorld)) { |
| 179 // All possible values are contained in the set of allowed values. |
| 180 // Note that we exploit the fact that [typeAsMask] is an exact |
| 181 // representation of [type], not an approximation. |
| 182 return AbstractBool.True; |
| 183 } |
| 184 // The value is neither contained in the type, nor disjoint from the type. |
| 185 return AbstractBool.Maybe; |
| 186 } |
| 187 // TODO(asgerf): Support function types, and what else might be missing. |
| 188 return AbstractBool.Maybe; |
| 189 } |
| 190 } |
| 191 |
| 192 class ConstantPropagationLattice<T> { |
| 193 final TypeSystem<T> typeSystem; |
| 194 final ConstantSystem constantSystem; |
| 195 final types.DartTypes dartTypes; |
| 196 |
| 197 ConstantPropagationLattice(this.typeSystem, |
| 198 this.constantSystem, |
| 199 this.dartTypes); |
| 200 |
| 201 final _AbstractValue<T> nothing = new _AbstractValue<T>.nothing(); |
| 202 |
| 203 _AbstractValue<T> constant(ConstantValue value) { |
| 204 return new _AbstractValue<T>.constantValue(value, |
| 205 typeSystem.getTypeOf(value)); |
| 206 } |
| 207 |
| 208 _AbstractValue<T> nonConstant(T type) { |
| 209 return new _AbstractValue<T>.nonConstant(type); |
| 210 } |
| 211 |
| 212 _AbstractValue<T> get anything { |
| 213 return new _AbstractValue<T>.nonConstant(typeSystem.dynamicType); |
| 214 } |
| 215 |
| 216 /// Returns whether the given [value] is an instance of [type]. |
| 217 /// |
| 218 /// Since [value] and [type] are not always known, [AbstractBool.Maybe] is |
| 219 /// returned if the answer is not known. |
| 220 /// |
| 221 /// [AbstractBool.Nothing] is returned if [value] is nothing. |
| 222 /// |
| 223 /// If [allowNull] is true, `null` is considered to an instance of anything, |
| 224 /// otherwise it is only considered an instance of [Object], [dynamic], and |
| 225 /// [Null]. |
| 226 AbstractBool isSubtypeOf(_AbstractValue<T> value, |
| 227 types.DartType type, |
| 228 {bool allowNull}) { |
| 229 assert(allowNull != null); |
| 230 if (value.isNothing) { |
| 231 return AbstractBool.Nothing; |
| 232 } |
| 233 if (value.isConstant) { |
| 234 if (value.constant.isNull) { |
| 235 if (allowNull || |
| 236 type.isObject || |
| 237 type.isDynamic || |
| 238 type == dartTypes.coreTypes.nullType) { |
| 239 return AbstractBool.True; |
| 240 } |
| 241 if (type is types.TypeVariableType) { |
| 242 return AbstractBool.Maybe; |
| 243 } |
| 244 return AbstractBool.False; |
| 245 } |
| 246 types.DartType valueType = value.constant.getType(dartTypes.coreTypes); |
| 247 if (constantSystem.isSubtype(dartTypes, valueType, type)) { |
| 248 return AbstractBool.True; |
| 249 } |
| 250 if (!dartTypes.isPotentialSubtype(valueType, type)) { |
| 251 return AbstractBool.False; |
| 252 } |
| 253 return AbstractBool.Maybe; |
| 254 } |
| 255 return typeSystem.isSubtypeOf(value.type, type, allowNull: allowNull); |
| 256 } |
| 257 |
| 258 /// Returns the possible results of applying [operator] to [value], |
| 259 /// assuming the operation does not throw. |
| 260 /// |
| 261 /// Because we do not explicitly track thrown values, we currently use the |
| 262 /// convention that constant values are returned from this method only |
| 263 /// if the operation is known not to throw. |
| 264 _AbstractValue<T> unaryOp(UnaryOperator operator, |
| 265 _AbstractValue<T> value) { |
| 266 // TODO(asgerf): Also return information about whether this can throw? |
| 267 if (value.isNothing) { |
| 268 return nothing; |
| 269 } |
| 270 if (value.isConstant) { |
| 271 UnaryOperation operation = constantSystem.lookupUnary(operator); |
| 272 ConstantValue result = operation.fold(value.constant); |
| 273 if (result == null) return anything; |
| 274 return constant(result); |
| 275 } |
| 276 return anything; // TODO(asgerf): Look up type. |
| 277 } |
| 278 |
| 279 /// Returns the possible results of applying [operator] to [left], [right], |
| 280 /// assuming the operation does not throw. |
| 281 /// |
| 282 /// Because we do not explicitly track thrown values, we currently use the |
| 283 /// convention that constant values are returned from this method only |
| 284 /// if the operation is known not to throw. |
| 285 _AbstractValue<T> binaryOp(BinaryOperator operator, |
| 286 _AbstractValue<T> left, |
| 287 _AbstractValue<T> right) { |
| 288 if (left.isNothing || right.isNothing) { |
| 289 return nothing; |
| 290 } |
| 291 if (left.isConstant && right.isConstant) { |
| 292 BinaryOperation operation = constantSystem.lookupBinary(operator); |
| 293 ConstantValue result = operation.fold(left.constant, right.constant); |
| 294 if (result == null) return anything; |
| 295 return constant(result); |
| 296 } |
| 297 return anything; // TODO(asgerf): Look up type. |
| 298 } |
| 143 } | 299 } |
| 144 | 300 |
| 145 /** | 301 /** |
| 146 * Propagates types (including value types for constants) throughout the IR, and | 302 * Propagates types (including value types for constants) throughout the IR, and |
| 147 * replaces branches with fixed jumps as well as side-effect free expressions | 303 * replaces branches with fixed jumps as well as side-effect free expressions |
| 148 * with known constant results. | 304 * with known constant results. |
| 149 * | 305 * |
| 150 * Should be followed by the [ShrinkingReducer] pass. | 306 * Should be followed by the [ShrinkingReducer] pass. |
| 151 * | 307 * |
| 152 * Implemented according to 'Constant Propagation with Conditional Branches' | 308 * Implemented according to 'Constant Propagation with Conditional Branches' |
| 153 * by Wegman, Zadeck. | 309 * by Wegman, Zadeck. |
| 154 */ | 310 */ |
| 155 class TypePropagator<T> extends Pass { | 311 class TypePropagator<T> extends Pass { |
| 156 String get passName => 'Sparse constant propagation'; | 312 String get passName => 'Sparse constant propagation'; |
| 157 | 313 |
| 158 final types.DartTypes _dartTypes; | 314 final types.DartTypes _dartTypes; |
| 159 | 315 |
| 160 // The constant system is used for evaluation of expressions with constant | 316 // The constant system is used for evaluation of expressions with constant |
| 161 // arguments. | 317 // arguments. |
| 162 final ConstantSystem _constantSystem; | 318 final ConstantSystem _constantSystem; |
| 163 final TypeSystem _typeSystem; | 319 final TypeSystem _typeSystem; |
| 164 final dart2js.InternalErrorFunction _internalError; | 320 final dart2js.InternalErrorFunction _internalError; |
| 165 final Map<Node, _AbstractValue> _types; | 321 final Map<Primitive, _AbstractValue> _types = <Primitive, _AbstractValue>{}; |
| 166 | 322 |
| 167 TypePropagator(this._dartTypes, | 323 TypePropagator(this._dartTypes, |
| 168 this._constantSystem, | 324 this._constantSystem, |
| 169 this._typeSystem, | 325 this._typeSystem, |
| 170 this._internalError) | 326 this._internalError); |
| 171 : _types = <Node, _AbstractValue>{}; | |
| 172 | 327 |
| 173 @override | 328 @override |
| 174 void rewrite(FunctionDefinition root) { | 329 void rewrite(FunctionDefinition root) { |
| 175 // Set all parent pointers. | 330 // Set all parent pointers. |
| 176 new ParentVisitor().visit(root); | 331 new ParentVisitor().visit(root); |
| 177 | 332 |
| 333 ConstantPropagationLattice<T> lattice = new ConstantPropagationLattice<T>( |
| 334 _typeSystem, _constantSystem, _dartTypes); |
| 335 Map<Expression, ConstantValue> replacements = <Expression, ConstantValue>{}; |
| 336 |
| 178 // Analyze. In this phase, the entire term is analyzed for reachability | 337 // Analyze. In this phase, the entire term is analyzed for reachability |
| 179 // and the abstract value of each expression. | 338 // and the abstract value of each expression. |
| 180 _TypePropagationVisitor<T> analyzer = new _TypePropagationVisitor<T>( | 339 _TypePropagationVisitor<T> analyzer = new _TypePropagationVisitor<T>( |
| 181 _constantSystem, | 340 lattice, |
| 182 _typeSystem, | |
| 183 _types, | 341 _types, |
| 184 _internalError, | 342 replacements, |
| 185 _dartTypes); | 343 _internalError); |
| 186 | 344 |
| 187 analyzer.analyze(root); | 345 analyzer.analyze(root); |
| 188 | 346 |
| 189 // Transform. Uses the data acquired in the previous analysis phase to | 347 // Transform. Uses the data acquired in the previous analysis phase to |
| 190 // replace branches with fixed targets and side-effect-free expressions | 348 // replace branches with fixed targets and side-effect-free expressions |
| 191 // with constant results. | 349 // with constant results or existing values that are in scope. |
| 192 _TransformingVisitor<T> transformer = new _TransformingVisitor<T>( | 350 _TransformingVisitor<T> transformer = new _TransformingVisitor<T>( |
| 193 analyzer.reachableNodes, analyzer.values, _internalError, _typeSystem); | 351 lattice, |
| 352 analyzer.reachableNodes, |
| 353 analyzer.values, |
| 354 replacements, |
| 355 _internalError); |
| 194 transformer.transform(root); | 356 transformer.transform(root); |
| 195 } | 357 } |
| 196 | 358 |
| 197 getType(Node node) => _types[node]; | 359 getType(Node node) => _types[node]; |
| 198 } | 360 } |
| 199 | 361 |
| 200 /** | 362 /** |
| 201 * Uses the information from a preceding analysis pass in order to perform the | 363 * Uses the information from a preceding analysis pass in order to perform the |
| 202 * actual transformations on the CPS graph. | 364 * actual transformations on the CPS graph. |
| 203 */ | 365 */ |
| 204 class _TransformingVisitor<T> extends RecursiveVisitor { | 366 class _TransformingVisitor<T> extends RecursiveVisitor { |
| 205 final Set<Node> reachable; | 367 final Set<Node> reachable; |
| 206 final Map<Node, _AbstractValue> values; | 368 final Map<Node, _AbstractValue> values; |
| 207 final TypeSystem<T> typeSystem; | 369 final Map<Expression, ConstantValue> replacements; |
| 370 final ConstantPropagationLattice<T> valueLattice; |
| 371 |
| 372 TypeSystem<T> get typeSystem => valueLattice.typeSystem; |
| 208 | 373 |
| 209 final dart2js.InternalErrorFunction internalError; | 374 final dart2js.InternalErrorFunction internalError; |
| 210 | 375 |
| 211 _TransformingVisitor(this.reachable, | 376 _TransformingVisitor(this.valueLattice, |
| 377 this.reachable, |
| 212 this.values, | 378 this.values, |
| 213 this.internalError, | 379 this.replacements, |
| 214 this.typeSystem); | 380 this.internalError); |
| 215 | 381 |
| 216 void transform(FunctionDefinition root) { | 382 void transform(FunctionDefinition root) { |
| 217 visit(root); | 383 visit(root); |
| 218 } | 384 } |
| 219 | 385 |
| 386 Constant makeConstantPrimitive(ConstantValue constant) { |
| 387 ConstantExpression constExp = |
| 388 const ConstantExpressionCreator().convert(constant); |
| 389 Constant primitive = new Constant(constExp, constant); |
| 390 values[primitive] = new _AbstractValue.constantValue(constant, |
| 391 typeSystem.getTypeOf(constant)); |
| 392 return primitive; |
| 393 } |
| 394 |
| 220 /// Given an expression with a known constant result and a continuation, | 395 /// Given an expression with a known constant result and a continuation, |
| 221 /// replaces the expression by a new LetPrim / InvokeContinuation construct. | 396 /// replaces the expression by a new LetPrim / InvokeContinuation construct. |
| 222 /// `unlink` is a closure responsible for unlinking all removed references. | 397 /// `unlink` is a closure responsible for unlinking all removed references. |
| 223 LetPrim constifyExpression(Expression node, | 398 LetPrim constifyExpression(Expression node, |
| 224 Continuation continuation, | 399 Continuation continuation, |
| 225 void unlink()) { | 400 void unlink()) { |
| 226 _AbstractValue value = values[node]; | 401 ConstantValue constant = replacements[node]; |
| 227 if (value == null || !value.isConstant) { | 402 if (constant == null) return null; |
| 228 return null; | |
| 229 } | |
| 230 | 403 |
| 231 assert(continuation.parameters.length == 1); | 404 assert(continuation.parameters.length == 1); |
| 405 InteriorNode parent = node.parent; |
| 406 Constant primitive = makeConstantPrimitive(constant); |
| 407 LetPrim letPrim = new LetPrim(primitive); |
| 232 | 408 |
| 233 // Set up the replacement structure. | |
| 234 PrimitiveConstantValue primitiveConstant = value.constant; | |
| 235 ConstantExpression constExp = | |
| 236 const ConstantExpressionCreator().convert(primitiveConstant); | |
| 237 Constant constant = new Constant(constExp, primitiveConstant); | |
| 238 LetPrim letPrim = new LetPrim(constant); | |
| 239 InvokeContinuation invoke = | 409 InvokeContinuation invoke = |
| 240 new InvokeContinuation(continuation, <Primitive>[constant]); | 410 new InvokeContinuation(continuation, <Primitive>[primitive]); |
| 241 | 411 parent.body = letPrim; |
| 242 invoke.parent = constant.parent = letPrim; | |
| 243 letPrim.body = invoke; | 412 letPrim.body = invoke; |
| 244 | 413 invoke.parent = letPrim; |
| 245 // Replace the method invocation. | |
| 246 | |
| 247 InteriorNode parent = node.parent; | |
| 248 letPrim.parent = parent; | 414 letPrim.parent = parent; |
| 249 parent.body = letPrim; | |
| 250 | 415 |
| 251 unlink(); | 416 unlink(); |
| 252 | 417 |
| 253 return letPrim; | 418 return letPrim; |
| 254 } | 419 } |
| 255 | 420 |
| 256 // A branch can be eliminated and replaced by an invocation if only one of | 421 // A branch can be eliminated and replaced by an invocation if only one of |
| 257 // the possible continuations is reachable. Removal often leads to both dead | 422 // the possible continuations is reachable. Removal often leads to both dead |
| 258 // primitives (the condition variable) and dead continuations (the unreachable | 423 // primitives (the condition variable) and dead continuations (the unreachable |
| 259 // branch), which are both removed by the shrinking reductions pass. | 424 // branch), which are both removed by the shrinking reductions pass. |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 327 node.arguments.forEach((Reference ref) => ref.unlink()); | 492 node.arguments.forEach((Reference ref) => ref.unlink()); |
| 328 }); | 493 }); |
| 329 | 494 |
| 330 if (letPrim == null) { | 495 if (letPrim == null) { |
| 331 super.visitConcatenateStrings(node); | 496 super.visitConcatenateStrings(node); |
| 332 } else { | 497 } else { |
| 333 visitLetPrim(letPrim); | 498 visitLetPrim(letPrim); |
| 334 } | 499 } |
| 335 } | 500 } |
| 336 | 501 |
| 337 // See [visitInvokeMethod]. | 502 void visitTypeCast(TypeCast node) { |
| 338 void visitTypeOperator(TypeOperator node) { | |
| 339 Continuation cont = node.continuation.definition; | 503 Continuation cont = node.continuation.definition; |
| 340 LetPrim letPrim = constifyExpression(node, cont, () { | 504 InteriorNode parent = node.parent; |
| 341 node.value.unlink(); | |
| 342 node.typeArguments.forEach((Reference ref) => ref.unlink()); | |
| 343 node.continuation.unlink(); | |
| 344 }); | |
| 345 | 505 |
| 346 if (letPrim == null) { | 506 _AbstractValue<T> value = getValue(node.value.definition); |
| 347 super.visitTypeOperator(node); | 507 switch (valueLattice.isSubtypeOf(value, node.type, allowNull: true)) { |
| 348 } else { | 508 case AbstractBool.Maybe: |
| 349 visitLetPrim(letPrim); | 509 case AbstractBool.Nothing: |
| 510 break; |
| 511 |
| 512 case AbstractBool.True: |
| 513 // Cast always succeeds, replace it with InvokeContinuation. |
| 514 InvokeContinuation invoke = |
| 515 new InvokeContinuation.fromCall(node.continuation, node.value); |
| 516 parent.body = invoke; |
| 517 invoke.parent = parent; |
| 518 super.visitInvokeContinuation(invoke); |
| 519 return; |
| 520 |
| 521 case AbstractBool.False: |
| 522 // Cast always fails, remove unreachable continuation body. |
| 523 assert(!reachable.contains(cont)); |
| 524 RemovalVisitor.remove(cont.body); |
| 525 cont.body = new Unreachable()..parent = cont; |
| 526 break; |
| 350 } | 527 } |
| 528 |
| 529 super.visitTypeCast(node); |
| 351 } | 530 } |
| 352 | 531 |
| 353 _AbstractValue<T> getValue(Primitive primitive) { | 532 _AbstractValue<T> getValue(Primitive primitive) { |
| 354 _AbstractValue<T> value = values[primitive]; | 533 _AbstractValue<T> value = values[primitive]; |
| 355 return value == null ? new _AbstractValue.nothing() : value; | 534 return value == null ? new _AbstractValue.nothing() : value; |
| 356 } | 535 } |
| 357 | 536 |
| 358 void visitIdentical(Identical node) { | 537 void visitIdentical(Identical node) { |
| 359 Primitive left = node.left.definition; | 538 Primitive left = node.left.definition; |
| 360 Primitive right = node.right.definition; | 539 Primitive right = node.right.definition; |
| 361 _AbstractValue<T> leftValue = getValue(left); | 540 _AbstractValue<T> leftValue = getValue(left); |
| 362 _AbstractValue<T> rightValue = getValue(right); | 541 _AbstractValue<T> rightValue = getValue(right); |
| 363 // Replace identical(x, true) by x when x is known to be a boolean. | 542 // Replace identical(x, true) by x when x is known to be a boolean. |
| 364 if (leftValue.isDefinitelyBool(typeSystem) && | 543 if (leftValue.isDefinitelyBool(typeSystem) && |
| 365 rightValue.isConstant && | 544 rightValue.isConstant && |
| 366 rightValue.constant.isTrue) { | 545 rightValue.constant.isTrue) { |
| 367 left.substituteFor(node); | 546 left.substituteFor(node); |
| 368 } | 547 } |
| 369 } | 548 } |
| 549 |
| 550 void visitLetPrim(LetPrim node) { |
| 551 _AbstractValue<T> value = getValue(node.primitive); |
| 552 if (node.primitive is! Constant && value.isConstant) { |
| 553 // If the value is a known constant, compile it as a constant. |
| 554 Constant newPrim = makeConstantPrimitive(value.constant); |
| 555 LetPrim newLet = new LetPrim(newPrim); |
| 556 node.parent.body = newLet; |
| 557 newLet.body = node.body; |
| 558 node.body.parent = newLet; |
| 559 newLet.parent = node.parent; |
| 560 newPrim.substituteFor(node.primitive); |
| 561 RemovalVisitor.remove(node.primitive); |
| 562 visit(newLet.body); |
| 563 } else { |
| 564 super.visitLetPrim(node); |
| 565 } |
| 566 } |
| 370 } | 567 } |
| 371 | 568 |
| 372 /** | 569 /** |
| 373 * Runs an analysis pass on the given function definition in order to detect | 570 * Runs an analysis pass on the given function definition in order to detect |
| 374 * const-ness as well as reachability, both of which are used in the subsequent | 571 * const-ness as well as reachability, both of which are used in the subsequent |
| 375 * transformation pass. | 572 * transformation pass. |
| 376 */ | 573 */ |
| 377 class _TypePropagationVisitor<T> implements Visitor { | 574 class _TypePropagationVisitor<T> implements Visitor { |
| 378 // The node worklist stores nodes that are both reachable and need to be | 575 // The node worklist stores nodes that are both reachable and need to be |
| 379 // processed, but have not been processed yet. Using a worklist avoids deep | 576 // processed, but have not been processed yet. Using a worklist avoids deep |
| 380 // recursion. | 577 // recursion. |
| 381 // The node worklist and the reachable set operate in concert: nodes are | 578 // The node worklist and the reachable set operate in concert: nodes are |
| 382 // only ever added to the worklist when they have not yet been marked as | 579 // only ever added to the worklist when they have not yet been marked as |
| 383 // reachable, and adding a node to the worklist is always followed by marking | 580 // reachable, and adding a node to the worklist is always followed by marking |
| 384 // it reachable. | 581 // it reachable. |
| 385 // TODO(jgruber): Storing reachability per-edge instead of per-node would | 582 // TODO(jgruber): Storing reachability per-edge instead of per-node would |
| 386 // allow for further optimizations. | 583 // allow for further optimizations. |
| 387 final List<Node> nodeWorklist = <Node>[]; | 584 final List<Node> nodeWorklist = <Node>[]; |
| 388 final Set<Node> reachableNodes = new Set<Node>(); | 585 final Set<Node> reachableNodes = new Set<Node>(); |
| 389 | 586 |
| 390 // The definition workset stores all definitions which need to be reprocessed | 587 // The definition workset stores all definitions which need to be reprocessed |
| 391 // since their lattice value has changed. | 588 // since their lattice value has changed. |
| 392 final Set<Definition> defWorkset = new Set<Definition>(); | 589 final Set<Definition> defWorkset = new Set<Definition>(); |
| 393 | 590 |
| 394 final ConstantSystem constantSystem; | 591 final ConstantPropagationLattice<T> valueLattice; |
| 395 final TypeSystem<T> typeSystem; | |
| 396 final dart2js.InternalErrorFunction internalError; | 592 final dart2js.InternalErrorFunction internalError; |
| 397 final types.DartTypes _dartTypes; | 593 |
| 594 TypeSystem get typeSystem => valueLattice.typeSystem; |
| 398 | 595 |
| 399 _AbstractValue<T> nothing = new _AbstractValue.nothing(); | 596 _AbstractValue<T> nothing = new _AbstractValue.nothing(); |
| 400 | 597 |
| 401 _AbstractValue<T> nonConstant([T type]) { | 598 _AbstractValue<T> nonConstant([T type]) { |
| 402 if (type == null) { | 599 if (type == null) { |
| 403 type = typeSystem.dynamicType; | 600 type = typeSystem.dynamicType; |
| 404 } | 601 } |
| 405 return new _AbstractValue<T>.nonConstant(type); | 602 return new _AbstractValue<T>.nonConstant(type); |
| 406 } | 603 } |
| 407 | 604 |
| 408 _AbstractValue<T> constantValue(ConstantValue constant, T type) { | 605 _AbstractValue<T> constantValue(ConstantValue constant, T type) { |
| 409 return new _AbstractValue<T>.constantValue(constant, type); | 606 return new _AbstractValue<T>.constantValue(constant, type); |
| 410 } | 607 } |
| 411 | 608 |
| 412 // Stores the current lattice value for nodes. Note that it contains not only | 609 // Stores the current lattice value for nodes. Note that it contains not only |
| 413 // definitions as keys, but also expressions such as method invokes. | 610 // definitions as keys, but also expressions such as method invokes. |
| 414 // Access through [getValue] and [setValue]. | 611 // Access through [getValue] and [setValue]. |
| 415 final Map<Node, _AbstractValue<T>> values; | 612 final Map<Primitive, _AbstractValue<T>> values; |
| 416 | 613 |
| 417 _TypePropagationVisitor(this.constantSystem, | 614 /// Expressions that invoke their call continuation with a constant value |
| 418 TypeSystem typeSystem, | 615 /// and without any side effects. These can be replaced by the constant. |
| 616 final Map<Expression, ConstantValue> replacements; |
| 617 |
| 618 _TypePropagationVisitor(this.valueLattice, |
| 419 this.values, | 619 this.values, |
| 420 this.internalError, | 620 this.replacements, |
| 421 this._dartTypes) | 621 this.internalError); |
| 422 : this.typeSystem = typeSystem; | |
| 423 | 622 |
| 424 void analyze(FunctionDefinition root) { | 623 void analyze(FunctionDefinition root) { |
| 425 reachableNodes.clear(); | 624 reachableNodes.clear(); |
| 426 defWorkset.clear(); | 625 defWorkset.clear(); |
| 427 nodeWorklist.clear(); | 626 nodeWorklist.clear(); |
| 428 | 627 |
| 429 // Initially, only the root node is reachable. | 628 // Initially, only the root node is reachable. |
| 430 setReachable(root); | 629 setReachable(root); |
| 431 | 630 |
| 432 while (true) { | 631 while (true) { |
| (...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 553 Definition def = node.arguments[i].definition; | 752 Definition def = node.arguments[i].definition; |
| 554 _AbstractValue<T> cell = getValue(def); | 753 _AbstractValue<T> cell = getValue(def); |
| 555 setValue(cont.parameters[i], cell); | 754 setValue(cont.parameters[i], cell); |
| 556 } | 755 } |
| 557 } | 756 } |
| 558 | 757 |
| 559 void visitInvokeMethod(InvokeMethod node) { | 758 void visitInvokeMethod(InvokeMethod node) { |
| 560 Continuation cont = node.continuation.definition; | 759 Continuation cont = node.continuation.definition; |
| 561 setReachable(cont); | 760 setReachable(cont); |
| 562 | 761 |
| 563 /// Sets the value of both the current node and the target continuation | 762 /// Sets the value of the target continuation parameter, and possibly |
| 564 /// parameter. | 763 /// try to replace the whole invocation with a constant. |
| 565 void setValues(_AbstractValue<T> updateValue) { | 764 void setResult(_AbstractValue<T> updateValue, {bool canReplace: false}) { |
| 566 setValue(node, updateValue); | |
| 567 Parameter returnValue = cont.parameters[0]; | 765 Parameter returnValue = cont.parameters[0]; |
| 568 setValue(returnValue, updateValue); | 766 setValue(returnValue, updateValue); |
| 767 if (canReplace && updateValue.isConstant) { |
| 768 replacements[node] = updateValue.constant; |
| 769 } else { |
| 770 // A previous iteration might have tried to replace this. |
| 771 replacements.remove(node); |
| 772 } |
| 569 } | 773 } |
| 570 | 774 |
| 571 _AbstractValue<T> lhs = getValue(node.receiver.definition); | 775 _AbstractValue<T> lhs = getValue(node.receiver.definition); |
| 572 if (lhs.isNothing) { | 776 if (lhs.isNothing) { |
| 573 return; // And come back later. | 777 return; // And come back later. |
| 574 } else if (lhs.isNonConst) { | 778 } |
| 575 setValues(nonConstant(typeSystem.getSelectorReturnType(node.selector))); | 779 if (!node.selector.isOperator) { |
| 576 return; | |
| 577 } else if (!node.selector.isOperator) { | |
| 578 // TODO(jgruber): Handle known methods on constants such as String.length. | 780 // TODO(jgruber): Handle known methods on constants such as String.length. |
| 579 setValues(nonConstant()); | 781 setResult(nonConstant(typeSystem.getSelectorReturnType(node.selector))); |
| 580 return; | 782 return; |
| 581 } | 783 } |
| 582 | 784 |
| 785 // TODO(asgerf): Support constant folding on intercepted calls! |
| 786 |
| 583 // Calculate the resulting constant if possible. | 787 // Calculate the resulting constant if possible. |
| 584 ConstantValue result; | 788 _AbstractValue<T> result; |
| 585 String opname = node.selector.name; | 789 String opname = node.selector.name; |
| 586 if (node.selector.argumentCount == 0) { | 790 if (node.selector.argumentCount == 0) { |
| 587 // Unary operator. | 791 // Unary operator. |
| 588 | |
| 589 if (opname == "unary-") { | 792 if (opname == "unary-") { |
| 590 opname = "-"; | 793 opname = "-"; |
| 591 } | 794 } |
| 592 UnaryOperation operation = constantSystem.lookupUnary( | 795 UnaryOperator operator = UnaryOperator.parse(opname); |
| 593 UnaryOperator.parse(opname)); | 796 result = valueLattice.unaryOp(operator, lhs); |
| 594 if (operation != null) { | |
| 595 result = operation.fold(lhs.constant); | |
| 596 } | |
| 597 } else if (node.selector.argumentCount == 1) { | 797 } else if (node.selector.argumentCount == 1) { |
| 598 // Binary operator. | 798 // Binary operator. |
| 599 | |
| 600 _AbstractValue<T> rhs = getValue(node.arguments[0].definition); | 799 _AbstractValue<T> rhs = getValue(node.arguments[0].definition); |
| 601 if (!rhs.isConstant) { | 800 BinaryOperator operator = BinaryOperator.parse(opname); |
| 602 setValues(nonConstant()); | 801 result = valueLattice.binaryOp(operator, lhs, rhs); |
| 603 return; | |
| 604 } | |
| 605 | |
| 606 BinaryOperation operation = constantSystem.lookupBinary( | |
| 607 BinaryOperator.parse(opname)); | |
| 608 if (operation != null) { | |
| 609 result = operation.fold(lhs.constant, rhs.constant); | |
| 610 } | |
| 611 } | 802 } |
| 612 | 803 |
| 613 // Update value of the continuation parameter. Again, this is effectively | 804 // Update value of the continuation parameter. Again, this is effectively |
| 614 // a phi. | 805 // a phi. |
| 615 if (result == null) { | 806 if (result == null) { |
| 616 setValues(nonConstant()); | 807 setResult(nonConstant()); |
| 617 } else { | 808 } else { |
| 618 T type = typeSystem.getTypeOf(result); | 809 setResult(result, canReplace: true); |
| 619 setValues(constantValue(result, type)); | |
| 620 } | 810 } |
| 621 } | 811 } |
| 622 | 812 |
| 623 void visitInvokeMethodDirectly(InvokeMethodDirectly node) { | 813 void visitInvokeMethodDirectly(InvokeMethodDirectly node) { |
| 624 Continuation cont = node.continuation.definition; | 814 Continuation cont = node.continuation.definition; |
| 625 setReachable(cont); | 815 setReachable(cont); |
| 626 | 816 |
| 627 assert(cont.parameters.length == 1); | 817 assert(cont.parameters.length == 1); |
| 628 Parameter returnValue = cont.parameters[0]; | 818 Parameter returnValue = cont.parameters[0]; |
| 629 // TODO(karlklose): lookup the function and get ites return type. | 819 // TODO(karlklose): lookup the function and get ites return type. |
| 630 setValue(returnValue, nonConstant()); | 820 setValue(returnValue, nonConstant()); |
| 631 } | 821 } |
| 632 | 822 |
| 633 void visitInvokeConstructor(InvokeConstructor node) { | 823 void visitInvokeConstructor(InvokeConstructor node) { |
| 634 Continuation cont = node.continuation.definition; | 824 Continuation cont = node.continuation.definition; |
| 635 setReachable(cont); | 825 setReachable(cont); |
| 636 | 826 |
| 637 assert(cont.parameters.length == 1); | 827 assert(cont.parameters.length == 1); |
| 638 Parameter returnValue = cont.parameters[0]; | 828 Parameter returnValue = cont.parameters[0]; |
| 639 setValue(returnValue, nonConstant(typeSystem.getReturnType(node.target))); | 829 setValue(returnValue, nonConstant(typeSystem.getReturnType(node.target))); |
| 640 } | 830 } |
| 641 | 831 |
| 642 void visitConcatenateStrings(ConcatenateStrings node) { | 832 void visitConcatenateStrings(ConcatenateStrings node) { |
| 643 Continuation cont = node.continuation.definition; | 833 Continuation cont = node.continuation.definition; |
| 644 setReachable(cont); | 834 setReachable(cont); |
| 645 | 835 |
| 646 void setValues(_AbstractValue<T> updateValue) { | 836 /// Sets the value of the target continuation parameter, and possibly |
| 647 setValue(node, updateValue); | 837 /// try to replace the whole invocation with a constant. |
| 838 void setResult(_AbstractValue<T> updateValue, {bool canReplace: false}) { |
| 648 Parameter returnValue = cont.parameters[0]; | 839 Parameter returnValue = cont.parameters[0]; |
| 649 setValue(returnValue, updateValue); | 840 setValue(returnValue, updateValue); |
| 841 if (canReplace && updateValue.isConstant) { |
| 842 replacements[node] = updateValue.constant; |
| 843 } else { |
| 844 // A previous iteration might have tried to replace this. |
| 845 replacements.remove(node); |
| 846 } |
| 650 } | 847 } |
| 651 | 848 |
| 652 // TODO(jgruber): Currently we only optimize if all arguments are string | 849 // TODO(jgruber): Currently we only optimize if all arguments are string |
| 653 // constants, but we could also handle cases such as "foo${42}". | 850 // constants, but we could also handle cases such as "foo${42}". |
| 654 bool allStringConstants = node.arguments.every((Reference ref) { | 851 bool allStringConstants = node.arguments.every((Reference ref) { |
| 655 if (!(ref.definition is Constant)) { | 852 if (!(ref.definition is Constant)) { |
| 656 return false; | 853 return false; |
| 657 } | 854 } |
| 658 Constant constant = ref.definition; | 855 Constant constant = ref.definition; |
| 659 return constant != null && constant.value.isString; | 856 return constant != null && constant.value.isString; |
| 660 }); | 857 }); |
| 661 | 858 |
| 662 T type = typeSystem.stringType; | 859 T type = typeSystem.stringType; |
| 663 assert(cont.parameters.length == 1); | 860 assert(cont.parameters.length == 1); |
| 664 if (allStringConstants) { | 861 if (allStringConstants) { |
| 665 // All constant, we can concatenate ourselves. | 862 // All constant, we can concatenate ourselves. |
| 666 Iterable<String> allStrings = node.arguments.map((Reference ref) { | 863 Iterable<String> allStrings = node.arguments.map((Reference ref) { |
| 667 Constant constant = ref.definition; | 864 Constant constant = ref.definition; |
| 668 StringConstantValue stringConstant = constant.value; | 865 StringConstantValue stringConstant = constant.value; |
| 669 return stringConstant.primitiveValue.slowToString(); | 866 return stringConstant.primitiveValue.slowToString(); |
| 670 }); | 867 }); |
| 671 LiteralDartString dartString = new LiteralDartString(allStrings.join()); | 868 LiteralDartString dartString = new LiteralDartString(allStrings.join()); |
| 672 ConstantValue constant = new StringConstantValue(dartString); | 869 ConstantValue constant = new StringConstantValue(dartString); |
| 673 setValues(constantValue(constant, type)); | 870 setResult(constantValue(constant, type), canReplace: true); |
| 674 } else { | 871 } else { |
| 675 setValues(nonConstant(type)); | 872 setResult(nonConstant(type)); |
| 676 } | 873 } |
| 677 } | 874 } |
| 678 | 875 |
| 679 void visitThrow(Throw node) { | 876 void visitThrow(Throw node) { |
| 680 } | 877 } |
| 681 | 878 |
| 682 void visitRethrow(Rethrow node) { | 879 void visitRethrow(Rethrow node) { |
| 683 } | 880 } |
| 684 | 881 |
| 882 void visitUnreachable(Unreachable node) { |
| 883 } |
| 884 |
| 685 void visitNonTailThrow(NonTailThrow node) { | 885 void visitNonTailThrow(NonTailThrow node) { |
| 686 internalError(null, 'found non-tail throw after they were eliminated'); | 886 internalError(null, 'found non-tail throw after they were eliminated'); |
| 687 } | 887 } |
| 688 | 888 |
| 689 void visitBranch(Branch node) { | 889 void visitBranch(Branch node) { |
| 690 IsTrue isTrue = node.condition; | 890 IsTrue isTrue = node.condition; |
| 691 _AbstractValue<T> conditionCell = getValue(isTrue.value.definition); | 891 _AbstractValue<T> conditionCell = getValue(isTrue.value.definition); |
| 692 | 892 |
| 693 if (conditionCell.isNothing) { | 893 if (conditionCell.isNothing) { |
| 694 return; // And come back later. | 894 return; // And come back later. |
| 695 } else if (conditionCell.isNonConst) { | 895 } else if (conditionCell.isNonConst) { |
| 696 setReachable(node.trueContinuation.definition); | 896 setReachable(node.trueContinuation.definition); |
| 697 setReachable(node.falseContinuation.definition); | 897 setReachable(node.falseContinuation.definition); |
| 698 } else if (conditionCell.isConstant && !conditionCell.constant.isBool) { | 898 } else if (conditionCell.isConstant && !conditionCell.constant.isBool) { |
| 699 // Treat non-bool constants in condition as non-const since they result | 899 // Treat non-bool constants in condition as non-const since they result |
| 700 // in type errors in checked mode. | 900 // in type errors in checked mode. |
| 701 // TODO(jgruber): Default to false in unchecked mode. | 901 // TODO(jgruber): Default to false in unchecked mode. |
| 702 setReachable(node.trueContinuation.definition); | 902 setReachable(node.trueContinuation.definition); |
| 703 setReachable(node.falseContinuation.definition); | 903 setReachable(node.falseContinuation.definition); |
| 704 setValue(isTrue.value.definition, nonConstant(typeSystem.boolType)); | 904 setValue(isTrue.value.definition, nonConstant(typeSystem.boolType)); |
| 705 } else if (conditionCell.isConstant && conditionCell.constant.isBool) { | 905 } else if (conditionCell.isConstant && conditionCell.constant.isBool) { |
| 706 BoolConstantValue boolConstant = conditionCell.constant; | 906 BoolConstantValue boolConstant = conditionCell.constant; |
| 707 setReachable((boolConstant.isTrue) ? | 907 setReachable((boolConstant.isTrue) ? |
| 708 node.trueContinuation.definition : node.falseContinuation.definition); | 908 node.trueContinuation.definition : node.falseContinuation.definition); |
| 709 } | 909 } |
| 710 } | 910 } |
| 711 | 911 |
| 712 void visitTypeOperator(TypeOperator node) { | 912 void visitTypeTest(TypeTest node) { |
| 713 Continuation cont = node.continuation.definition; | 913 _AbstractValue<T> input = getValue(node.value.definition); |
| 714 setReachable(cont); | 914 T boolType = typeSystem.boolType; |
| 915 switch(valueLattice.isSubtypeOf(input, node.type, allowNull: false)) { |
| 916 case AbstractBool.Nothing: |
| 917 break; // And come back later. |
| 715 | 918 |
| 716 void setValues(_AbstractValue<T> updateValue) { | 919 case AbstractBool.True: |
| 717 setValue(node, updateValue); | 920 setValue(node, constantValue(new TrueConstantValue(), boolType)); |
| 718 Parameter returnValue = cont.parameters[0]; | 921 break; |
| 719 setValue(returnValue, updateValue); | |
| 720 } | |
| 721 | 922 |
| 722 if (node.isTypeCast) { | 923 case AbstractBool.False: |
| 723 // TODO(jgruber): Add support for `as` casts. | 924 setValue(node, constantValue(new FalseConstantValue(), boolType)); |
| 724 setValues(nonConstant()); | 925 break; |
| 725 return; | |
| 726 } | |
| 727 | 926 |
| 728 _AbstractValue<T> cell = getValue(node.value.definition); | 927 case AbstractBool.Maybe: |
| 729 if (cell.isNothing) { | 928 setValue(node, nonConstant(boolType)); |
| 730 return; // And come back later. | 929 break; |
| 731 } else if (cell.isConstant && node.type.kind == types.TypeKind.INTERFACE) { | |
| 732 // Receiver is a constant, perform is-checks at compile-time. | |
| 733 | |
| 734 types.InterfaceType checkedType = node.type; | |
| 735 ConstantValue constant = cell.constant; | |
| 736 types.DartType constantType = constant.getType(_dartTypes.coreTypes); | |
| 737 | |
| 738 T type = typeSystem.boolType; | |
| 739 _AbstractValue<T> result; | |
| 740 if (constant.isNull && | |
| 741 checkedType != _dartTypes.coreTypes.nullType && | |
| 742 checkedType != _dartTypes.coreTypes.objectType) { | |
| 743 // `(null is Type)` is true iff Type is in { Null, Object }. | |
| 744 result = constantValue(new FalseConstantValue(), type); | |
| 745 } else { | |
| 746 // Otherwise, perform a standard subtype check. | |
| 747 result = constantValue( | |
| 748 constantSystem.isSubtype(_dartTypes, constantType, checkedType) | |
| 749 ? new TrueConstantValue() | |
| 750 : new FalseConstantValue(), | |
| 751 type); | |
| 752 } | |
| 753 setValues(result); | |
| 754 } else { | |
| 755 setValues(nonConstant(typeSystem.boolType)); | |
| 756 } | 930 } |
| 757 } | 931 } |
| 758 | 932 |
| 933 void visitTypeCast(TypeCast node) { |
| 934 Continuation cont = node.continuation.definition; |
| 935 _AbstractValue<T> input = getValue(node.value.definition); |
| 936 switch (valueLattice.isSubtypeOf(input, node.type, allowNull: true)) { |
| 937 case AbstractBool.Nothing: |
| 938 break; // And come back later. |
| 939 |
| 940 case AbstractBool.True: |
| 941 setReachable(cont); |
| 942 setValue(cont.parameters.single, input); |
| 943 break; |
| 944 |
| 945 case AbstractBool.False: |
| 946 break; // Cast fails. Continuation should remain unreachable. |
| 947 |
| 948 case AbstractBool.Maybe: |
| 949 // TODO(asgerf): Narrow type of output to those that survive the cast. |
| 950 setReachable(cont); |
| 951 setValue(cont.parameters.single, input); |
| 952 break; |
| 953 } |
| 954 } |
| 955 |
| 759 void visitSetMutableVariable(SetMutableVariable node) { | 956 void visitSetMutableVariable(SetMutableVariable node) { |
| 760 setValue(node.variable.definition, getValue(node.value.definition)); | 957 setValue(node.variable.definition, getValue(node.value.definition)); |
| 761 setReachable(node.body); | 958 setReachable(node.body); |
| 762 } | 959 } |
| 763 | 960 |
| 764 void visitLiteralList(LiteralList node) { | 961 void visitLiteralList(LiteralList node) { |
| 765 // Constant lists are translated into (Constant ListConstant(...)) IR nodes, | 962 // Constant lists are translated into (Constant ListConstant(...)) IR nodes, |
| 766 // and thus LiteralList nodes are NonConst. | 963 // and thus LiteralList nodes are NonConst. |
| 767 setValue(node, nonConstant(typeSystem.listType)); | 964 setValue(node, nonConstant(typeSystem.listType)); |
| 768 } | 965 } |
| (...skipping 316 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1085 ConstantExpression visitString(StringConstantValue constant, arg) { | 1282 ConstantExpression visitString(StringConstantValue constant, arg) { |
| 1086 return new StringConstantExpression( | 1283 return new StringConstantExpression( |
| 1087 constant.primitiveValue.slowToString()); | 1284 constant.primitiveValue.slowToString()); |
| 1088 } | 1285 } |
| 1089 | 1286 |
| 1090 @override | 1287 @override |
| 1091 ConstantExpression visitType(TypeConstantValue constant, arg) { | 1288 ConstantExpression visitType(TypeConstantValue constant, arg) { |
| 1092 throw new UnsupportedError("ConstantExpressionCreator.visitType"); | 1289 throw new UnsupportedError("ConstantExpressionCreator.visitType"); |
| 1093 } | 1290 } |
| 1094 } | 1291 } |
| OLD | NEW |