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

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

Issue 1304383003: dart2js cps: Add path-sensitive types by inserting refinement IR nodes. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Rebase Created 5 years, 3 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
(Empty)
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
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.
4
5 library cps_ir.optimization.insert_refinements;
6
7 import 'optimizers.dart' show Pass;
8 import 'shrinking_reductions.dart' show ParentVisitor;
9 import 'cps_ir_nodes.dart';
10 import '../types/types.dart';
11 import '../types/constants.dart';
12 import '../constants/values.dart';
13 import '../world.dart';
14 import '../common/names.dart';
15 import '../universe/universe.dart';
16 import '../elements/elements.dart';
17
18 /// Inserts [Refinement] nodes in the IR to allow for sparse path-sensitive
19 /// type analysis in the [TypePropagator] pass.
20 ///
21 /// Refinement nodes are inserted at the arms of a [Branch] node with a
22 /// condition of form `x is T` or `x == CONST`.
23 ///
24 /// Refinement nodes are inserted after a method invocation to account for the
25 /// fact that the receiver cannot be null afterwards (in most cases).
26 ///
27 // TODO(asgerf): Generalize the non-null pattern to all selectors/classes?
28 class InsertRefinements extends RecursiveVisitor implements Pass {
29 String get passName => 'Insert refinement nodes';
30
31 Parameter thisParameter;
32 final TypesTask types;
33 final World world;
34 final TypeMask nonNullType;
35 final TypeMask nullType;
36
37 /// Maps unrefined primitives to its refinement currently in scope (if any).
38 final Map<Primitive, Refinement> refinementFor = <Primitive, Refinement>{};
39
40 InsertRefinements(this.types, World world)
41 : this.world = world,
42 nonNullType = new TypeMask.nonNullSubtype(world.objectClass, world),
43 nullType = new TypeMask.empty();
44
45 void rewrite(FunctionDefinition node) {
46 new ParentVisitor().visit(node);
47 thisParameter = node.thisParameter;
48 visit(node.body);
49 }
50
51 /// Updates references to the refer to the refinement currently in scope.
52 void processReference(Reference node) {
53 Refinement refined = refinementFor[node.definition];
54 if (refined != null) {
55 node.changeTo(refined);
56 }
57 }
58
59 /// True if [prim] might evaluate to null.
60 ///
61 /// Used to avoid inserting refinement nodes that are obviously redundant.
62 bool maybeNull(Primitive prim) {
63 if (prim is Constant) return !prim.value.isNull;
64 if (prim is Interceptor) return maybeNull(prim.input.definition);
65 if (prim is Refinement) {
66 if (!prim.type.isNullable) return false;
67 return maybeNull(prim.value.definition);
68 }
69 if (prim == thisParameter) return false;
70 return true;
71 }
72
73 /// True if an exception is thrown when the given selector is invoked on null.
74 bool failsOnNull(Selector selector) {
75 if (selector.isGetter) {
76 return selector.name != 'toString' &&
77 selector.name != 'hashCode' &&
78 selector.name != 'noSuchMethod' &&
79 selector.name != 'runtimeType';
80 } else {
81 return selector != Selectors.toString_ &&
82 selector != Selectors.hashCode_ &&
83 selector != Selectors.equals;
84 }
85 }
86
87 /// Moves the binding of [cont] to before [use].
88 ///
89 /// This is used to ensure that everything in scope at [use] is also in scope
90 /// inside [cont], so refinements can be inserted inside [cont] without
91 /// accidentally referencing a primitive out of scope.
92 ///
93 /// It is always safe to do this for single-use continuations, because
94 /// strictly more things are in scope at the use site, and there can't be any
95 /// other use of [cont] that might fall out of scope since there is only
96 /// that single use.
97 void sinkContinuationToUse(Continuation cont, Expression use) {
98 assert(cont.hasExactlyOneUse && cont.firstRef.parent == use);
99 LetCont let = cont.parent;
100 InteriorNode useParent = use.parent;
101 if (useParent == let) return;
102 if (let.continuations.length > 1) {
103 // Create a new LetCont binding only this continuation.
104 let.continuations.remove(cont);
105 let = new LetCont(cont, null);
106 cont.parent = let;
107 } else {
108 // Remove LetCont from current position.
109 InteriorNode letParent = let.parent;
110 letParent.body = let.body;
111 let.body.parent = letParent;
112 }
113
114 // Insert LetCont before use.
115 useParent.body = let;
116 let.body = use;
117 use.parent = let;
118 let.parent = useParent;
119 }
120
121 Primitive unfoldInterceptor(Primitive prim) {
122 return prim is Interceptor ? prim.input.definition : prim;
123 }
124
125 /// Enqueues [cont] for processing in a context where [refined] is the
126 /// current refinement for its value.
127 void pushRefinement(Continuation cont, Refinement refined) {
128 Primitive value = refined.effectiveDefinition;
129 Primitive currentRefinement = refinementFor[value];
130 pushAction(() {
131 refinementFor[value] = currentRefinement;
132 if (refined.hasNoUses) {
133 // Clean up refinements that are not used.
134 refined.value.unlink();
135 } else {
136 cont.body = new LetPrim(refined, cont.body);
137 refined.parent = cont.body;
138 refined.value.parent = refined;
139 }
140 });
141 push(cont);
142 pushAction(() {
143 refinementFor[value] = refined;
144 });
145 }
146
147 void visitInvokeMethod(InvokeMethod node) {
148 Continuation cont = node.continuation.definition;
149
150 // Update references to their current refined values.
151 processReference(node.receiver);
152 node.arguments.forEach(processReference);
153
154 // If the call is intercepted, we want to refine the actual receiver,
155 // not the interceptor.
156 Primitive receiver = unfoldInterceptor(node.receiver.definition);
157
158 // Sink the continuation to the call to ensure everything in scope
159 // here is also in scope inside the continuations.
160 sinkContinuationToUse(cont, node);
161
162 if (node.selector.isClosureCall) {
163 // Do not try to refine the receiver of closure calls; the class world
164 // does not know about closure classes.
165 push(cont);
166 } else {
167 // Filter away receivers that throw on this selector.
168 TypeMask type = world.allFunctions.receiverType(node.selector, node.mask);
169 pushRefinement(cont, new Refinement(receiver, type));
170 }
171 }
172
173 CallExpression getCallWithResult(Primitive prim) {
174 if (prim is Parameter && prim.parent is Continuation) {
175 Continuation cont = prim.parent;
176 if (cont.hasExactlyOneUse && cont.firstRef.parent is CallExpression) {
177 return cont.firstRef.parent;
178 }
179 }
180 return null;
181 }
182
183 bool isTrue(Primitive prim) {
184 return prim is Constant && prim.value.isTrue;
185 }
186
187 TypeMask getTypeOf(ConstantValue constant) {
188 return computeTypeMask(types.compiler, constant);
189 }
190
191 void visitBranch(Branch node) {
192 processReference(node.condition);
193 Primitive condition = node.condition.definition;
194 CallExpression call = getCallWithResult(condition);
195
196 Continuation trueCont = node.trueContinuation.definition;
197 Continuation falseCont = node.falseContinuation.definition;
198
199 // Sink both continuations to the Branch to ensure everything in scope
200 // here is also in scope inside the continuations.
201 sinkContinuationToUse(trueCont, node);
202 sinkContinuationToUse(falseCont, node);
203
204 // If the condition is an 'is' check, promote the checked value.
205 if (condition is TypeTest && condition.type.element is ClassElement) {
206 Primitive value = condition.value.definition;
207 ClassElement classElement = condition.type.element;
208 TypeMask type = new TypeMask.nonNullSubtype(classElement, world);
209 Primitive refinedValue = new Refinement(value, type);
210 pushRefinement(trueCont, refinedValue);
211 push(falseCont);
212 return;
213 }
214
215 // If the condition is comparison with a constant, promote the other value.
216 if (call is InvokeMethod && call.selector == Selectors.equals) {
217 Primitive first = call.arguments[0].definition;
218 Primitive second = call.arguments[1].definition;
219 if (second is Constant && second.value.isNull) {
220 Refinement refinedTrue = new Refinement(first, nullType);
221 Refinement refinedFalse = new Refinement(first, nonNullType);
222 pushRefinement(trueCont, refinedTrue);
223 pushRefinement(falseCont, refinedFalse);
224 return;
225 }
226 if (first is Constant && first.value.isNull) {
227 Refinement refinedTrue = new Refinement(second, nullType);
228 Refinement refinedFalse = new Refinement(second, nonNullType);
229 pushRefinement(trueCont, refinedTrue);
230 pushRefinement(falseCont, refinedFalse);
231 return;
232 }
233 // For non-null constants, we can only refine in the true branch,
234 // since TypeMask has no complement type for other constants.
235 if (second is Constant) {
236 Refinement refinedTrue = new Refinement(first, getTypeOf(second.value));
237 pushRefinement(trueCont, refinedTrue);
238 push(falseCont);
239 return;
240 }
241 if (first is Constant) {
242 Refinement refinedTrue = new Refinement(second, getTypeOf(first.value));
243 pushRefinement(trueCont, refinedTrue);
244 push(falseCont);
245 return;
246 }
247 }
248
249 push(trueCont);
250 push(falseCont);
251 }
252
253 @override
254 Expression traverseLetCont(LetCont node) {
255 for (Continuation cont in node.continuations) {
256 if (cont.hasExactlyOneUse &&
257 (cont.firstRef.parent is InvokeMethod ||
258 cont.firstRef.parent is Branch)) {
259 // Do not push the continuation here.
260 // visitInvokeMethod and visitBranch will do that.
261 } else {
262 push(cont);
263 }
264 }
265 return node.body;
266 }
267 }
OLDNEW
« no previous file with comments | « pkg/compiler/lib/src/cps_ir/cps_ir_tracer.dart ('k') | pkg/compiler/lib/src/cps_ir/optimizers.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698