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 |