Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(150)

Side by Side Diff: pkg/compiler/lib/src/cps_ir/type_propagation.dart

Issue 1153603006: dart2js cps: Type casts and related changes to type propagation. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Another typo in SExpression unstrngifier Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « pkg/compiler/lib/src/cps_ir/shrinking_reductions.dart ('k') | pkg/compiler/lib/src/dart_types.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698