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

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

Issue 1444363002: dart2js cps: Global value numbering and loop-invariant code motion. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Add comment Created 5 years, 1 month 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 library dart2js.cps_ir.gvn;
2
3 import 'cps_ir_nodes.dart';
4 import '../universe/side_effects.dart';
5 import '../elements/elements.dart';
6 import 'optimizers.dart' show Pass;
7 import 'loop_hierarchy.dart';
8 import 'loop_effects.dart';
9 import '../world.dart';
10 import '../compiler.dart' show Compiler;
11 import '../js_backend/js_backend.dart' show JavaScriptBackend;
12 import '../constants/values.dart';
13
14 /// Eliminates redundant primitives by reusing the value of another primitive
15 /// that is known to have the same result. Primitives are also hoisted out of
16 /// loops when possible.
17 ///
18 /// Reusing values can introduce new temporaries, which in some cases is more
19 /// expensive than recomputing the value on-demand. For example, pulling an
20 /// expression such as "n+1" out of a loop is generally not worth it.
21 /// Such primitives are said to be "trivial".
22 ///
23 /// Trivial primitives are shared on-demand, i.e. they are only shared if
24 /// this enables a non-trivial primitive to be hoisted out of a loop.
25 //
26 // TODO(asgerf): Enable hoisting across refinement guards when this is safe:
27 // - Determine the type required for a given primitive to be "safe"
28 // - Recompute the type of a primitive after hoisting.
29 // E.g. GetIndex on a String can become a GetIndex on an arbitrary
30 // indexable, which is still safe but the type may change
31 // - Since the new type may be worse, insert a refinement at the old
32 // definition site, so we do not degrade existing type information.
33 //
34 // TODO(asgerf): Put this pass at a better place in the pipeline. We currently
35 // cannot put it anywhere we want, because this pass relies on refinement
36 // nodes being present (for safety), whereas other passes rely on refinement
37 // nodes being absent (for simplicity & precision).
38 //
39 class GVN extends TrampolineRecursiveVisitor implements Pass {
40 String get passName => 'GVN';
41
42 final Compiler compiler;
43 JavaScriptBackend get backend => compiler.backend;
44 World get world => compiler.world;
45
46 final GvnTable gvnTable = new GvnTable();
47 GvnVectorBuilder gvnVectorBuilder;
48 LoopHierarchy loopHierarchy;
49 LoopSideEffects loopEffects;
50
51 /// Effect numbers at the given join point.
52 Map<Continuation, EffectNumbers> effectsAt = <Continuation, EffectNumbers>{};
53
54 /// The effect numbers at the current position (during traversal).
55 EffectNumbers effectNumbers = new EffectNumbers();
56
57 /// The loop currently enclosing the binding of a given primitive.
58 final Map<Primitive, Continuation> loopHeaderFor =
59 <Primitive, Continuation>{};
60
61 /// The loop to which a given trivial primitive can be hoisted.
62 final Map<Primitive, Continuation> potentialLoopHeaderFor =
63 <Primitive, Continuation>{};
64
65 /// The GVNs for primitives that have been hoisted outside the given loop.
66 ///
67 /// These should be removed from the environment when exiting the loop.
68 final Map<Continuation, List<int>> loopHoistedBindings =
69 <Continuation, List<int>>{};
70
71 /// Maps GVNs to a currently-in-scope binding for that value.
72 final Map<int, Primitive> environment = <int, Primitive>{};
73
74 /// Maps GVN'able primitives to their global value number.
75 final Map<Primitive, int> gvnFor = <Primitive, int>{};
76
77 Continuation currentLoopHeader;
78
79 GVN(this.compiler);
80
81 int _usedEffectNumbers = 0;
82 int makeNewEffect() => ++_usedEffectNumbers;
83
84 void rewrite(FunctionDefinition node) {
85 gvnVectorBuilder = new GvnVectorBuilder(gvnFor, backend);
86 loopHierarchy = new LoopHierarchy(node);
87 loopEffects =
88 new LoopSideEffects(node, world, loopHierarchy: loopHierarchy);
89 visit(node);
90 }
91
92 // ------------------ GLOBAL VALUE NUMBERING ---------------------
93
94 @override
95 Expression traverseLetPrim(LetPrim node) {
96 Expression next = node.body;
97 Primitive prim = node.primitive;
98
99 loopHeaderFor[prim] = currentLoopHeader;
100
101 if (prim is Refinement) {
102 // Do not share refinements (they have no runtime or code size cost), and
103 // do not put them in the GVN table because GvnVectorBuilder unfolds
104 // refinements by itself.
105 return next;
106 }
107
108 // Compute the GVN vector for this computation.
109 List vector = gvnVectorBuilder.make(prim, effectNumbers);
110
111 // Update effect numbers due to side effects.
112 // Do this after computing the GVN vector so the primitive's GVN is not
113 // influenced by its own side effects.
114 visit(prim);
115
116 if (vector == null) {
117 // The primitive is not GVN'able. Move on.
118 return next;
119 }
120
121 // Compute the GVN for this primitive.
122 int gvn = gvnTable.insert(vector);
123 gvnFor[prim] = gvn;
124
125 // Try to reuse a previously computed value with the same GVN.
126 Primitive existing = environment[gvn];
127 if (existing != null &&
128 prim.isSafeForElimination &&
129 !isTrivialPrimitive(prim)) {
130 if (prim is Interceptor) {
131 Interceptor interceptor = existing;
132 interceptor.interceptedClasses.addAll(prim.interceptedClasses);
133 interceptor.flags |= prim.flags;
134 }
135 existing.substituteFor(prim);
136 prim.destroy();
137 node.remove();
138 return next;
139 }
140
141 // If the primitive has no side effects, try to hoist it out of a loop.
142 if (prim.isSafeForElimination && currentLoopHeader != null) {
sra1 2015/11/17 05:41:13 Is there some way to move these 80 lines into thei
asgerf 2015/11/17 12:43:42 Done. I'm glad to be rid of the pyramid of doom co
143 // Find the depth of the outermost scope where we can bind the primitive
144 // without bringing a reference out of scope. -1 is the depth of the
145 // top-level scope.
146 int hoistDepth = -1;
147 List<Primitive> inputsHoistedOnDemand = <Primitive>[];
148 ReferenceVisitor.forEachReference(prim, (Reference ref) {
sra1 2015/11/17 05:41:14 We should tweak the name a little. On first readin
asgerf 2015/11/17 12:43:42 Changed to InputVisitor.forEach.
149 Primitive input = ref.definition;
150 if (canIgnoreRefinementGuards(prim)) {
151 input = input.effectiveDefinition;
152 }
153 Continuation loopHeader;
154 if (potentialLoopHeaderFor.containsKey(input)) {
155 // This is a reference to a value that can be hoisted further out than
156 // it currently is. If we decide to hoist [prim], we must also hoist
157 // such dependent values.
158 loopHeader = potentialLoopHeaderFor[input];
159 inputsHoistedOnDemand.add(input);
160 } else {
161 loopHeader = loopHeaderFor[input];
162 }
163 Continuation referencedLoop =
164 loopHierarchy.lowestCommonAncestor(loopHeader, currentLoopHeader);
165 int depth = loopHierarchy.getDepth(referencedLoop);
166 if (depth > hoistDepth) {
167 hoistDepth = depth;
168 }
169 });
170 if (hoistDepth != loopHierarchy.getDepth(currentLoopHeader)) {
171 // Walk up the loop hierarchy and check at every step that any heap
172 // dependencies can safely be hoisted out of the loop.
173 Continuation enclosingLoop = currentLoopHeader;
174 Continuation hoistTarget = null;
175 while (loopHierarchy.getDepth(enclosingLoop) > hoistDepth &&
176 canHoistHeapDependencyOutOfLoop(prim, enclosingLoop)) {
177 hoistTarget = enclosingLoop;
178 enclosingLoop = loopHierarchy.getEnclosingLoop(enclosingLoop);
179 }
180 if (hoistTarget != null) {
181 if (isTrivialPrimitive(prim)) {
182 // The overhead from introducting a temporary might be greater than
183 // the overhead of evaluating this primitive at every iteration.
184 // Only hoist if this enables hoisting of a non-trivial primitive.
185 potentialLoopHeaderFor[prim] = enclosingLoop;
186 return next;
187 } else {
188 LetCont loopBinding = hoistTarget.parent;
189
190 // The primitive may depend on values that have not yet been
191 // hoisted as far as they can. Hoist those now.
192 for (Primitive input in inputsHoistedOnDemand) {
193 hoistTrivialPrimitive(input, loopBinding, enclosingLoop);
194 }
195
196 // Hoist the primitive.
197 node.remove();
198 node.insertAbove(loopBinding);
199 loopHeaderFor[prim] = enclosingLoop;
200
201 // If a refinement guard was bypassed, use the best refinement
202 // currently in scope.
203 if (canIgnoreRefinementGuards(prim)) {
204 int target = loopHierarchy.getDepth(enclosingLoop);
205 ReferenceVisitor.forEachReference(prim, (Reference ref) {
206 Primitive input = ref.definition;
207 while (input is Refinement) {
208 Continuation loop = loopHeaderFor[input];
209 loop = loopHierarchy.lowestCommonAncestor(loop, enclosingLoop) ;
sra1 2015/11/17 05:41:14 line length
asgerf 2015/11/17 12:43:42 Done by extract method
210 if (loopHierarchy.getDepth(loop) <= target) break;
211 Refinement refinement = input;
212 input = refinement.value.definition;
213 }
214 ref.changeTo(input);
215 });
216 }
217
218 // Put the primitive in the environment while processing the loop.
219 environment[gvn] = prim;
220 loopHoistedBindings
221 .putIfAbsent(hoistTarget, () => <int>[])
222 .add(gvn);
223 return next;
224 }
225 }
226 }
227 }
228
229 // The primitive could not be hoisted. Put the primitive in the
230 // environment while processing the body of the LetPrim.
231 environment[gvn] = prim;
232 pushAction(() {
233 assert(environment[gvn] == prim);
234 environment[gvn] = existing;
235 });
236
237 return next;
238 }
239
240 /// If the given primitive is a trivial primitive that should be hoisted
241 /// on-demand, hoist it and its dependent values above [loopBinding].
242 void hoistTrivialPrimitive(Primitive prim,
243 LetCont loopBinding,
244 Continuation enclosingLoop) {
245 if (!potentialLoopHeaderFor.containsKey(prim)) return;
246 assert(isTrivialPrimitive(prim));
247
248 // The primitive might already be bound in an outer scope. Do not reloate
sra1 2015/11/17 05:41:14 reloate
asgerf 2015/11/17 12:43:42 Done.
249 // the primitive unless we are lifting it.
sra1 2015/11/17 05:41:14 Would this happen if the primitive is referenced a
asgerf 2015/11/17 12:43:42 Yes, this is exactly how it happens. I put your ex
250 Continuation currentLoop = loopHeaderFor[prim];
251 int currentDepth = loopHierarchy.getDepth(currentLoop);
252 int targetDepth = loopHierarchy.getDepth(enclosingLoop);
253 if (currentDepth <= targetDepth) return;
254
255 // Hoist the trivial primitives being depended on so they remain in scope.
256 ReferenceVisitor.forEachReference(prim, (Reference ref) {
257 hoistTrivialPrimitive(ref.definition, loopBinding, enclosingLoop);
258 });
259
260 // Move the primitive.
261 LetPrim binding = prim.parent;
262 binding.remove();
263 binding.insertAbove(loopBinding);
264 loopHeaderFor[prim] = enclosingLoop;
265
266 if (potentialLoopHeaderFor[prim] == enclosingLoop) {
267 potentialLoopHeaderFor.remove(prim);
268 }
269 }
270
271 bool canIgnoreRefinementGuards(Primitive primitive) {
272 return primitive is Interceptor;
273 }
274
275 /// Returns true if the given primitive is so cheap at runtime that it is
276 /// better to (redundantly) recompute it rather than introduce a temporary.
277 bool isTrivialPrimitive(Primitive primitive) {
278 return primitive is ApplyBuiltinOperator ||
279 primitive is Constant && isTrivialConstant(primitive.value);
280 }
281
282 /// Returns true if the given constant has almost no runtime cost.
283 bool isTrivialConstant(ConstantValue value) {
284 return value.isPrimitive || value.isDummy;
285 }
286
287 /// True if [element] is a final or constant field or a function.
288 bool isImmutable(Element element) {
289 if (element.isField && backend.isNative(element)) return false;
290 return element.isField && (element.isFinal || element.isConst) ||
291 element.isFunction;
292 }
293
294 /// Assuming [prim] has no side effects, returns true if it can safely
295 /// be hoisted out of [loop] without changing its value.
296 bool canHoistHeapDependencyOutOfLoop(Primitive prim, Continuation loop) {
297 assert(prim.isSafeForElimination);
298 if (prim is GetLength) {
299 return !loopEffects.loopChangesLength(loop);
300 } else if (prim is GetField && !isImmutable(prim.field)) {
301 return !loopEffects.getSideEffectsInLoop(loop).changesInstanceProperty();
302 } else if (prim is GetStatic && !isImmutable(prim.element)) {
303 return !loopEffects.getSideEffectsInLoop(loop).changesStaticProperty();
304 } else if (prim is GetIndex) {
305 return !loopEffects.getSideEffectsInLoop(loop).changesIndex();
306 } else {
307 return true;
308 }
309 }
310
311
312 // ------------------ TRAVERSAL AND EFFECT NUMBERING ---------------------
313 //
314 // These methods traverse the IR while updating the current effect numbers.
315 // They are not specific to GVN.
316 //
317 // TODO(asgerf): Avoid duplicated code for side effect analysis.
318 // Should be easier to fix once primitives and call expressions are the same.
319
320 void addSideEffects(SideEffects fx, {bool length: true}) {
321 if (fx.changesInstanceProperty()) {
322 effectNumbers.instanceField = makeNewEffect();
323 }
324 if (fx.changesStaticProperty()) {
325 effectNumbers.staticField = makeNewEffect();
326 }
327 if (fx.changesIndex()) {
328 effectNumbers.indexableContent = makeNewEffect();
329 }
330 if (length && fx.changesIndex()) {
331 effectNumbers.indexableLength = makeNewEffect();
332 }
333 }
334
335 void addAllSideEffects() {
336 effectNumbers.instanceField = makeNewEffect();
337 effectNumbers.staticField = makeNewEffect();
338 effectNumbers.indexableContent = makeNewEffect();
339 effectNumbers.indexableLength = makeNewEffect();
340 }
341
342 Expression traverseLetHandler(LetHandler node) {
343 // Assume any kind of side effects may occur in the try block.
344 effectsAt[node.handler] = new EffectNumbers()
345 ..instanceField = makeNewEffect()
346 ..staticField = makeNewEffect()
347 ..indexableContent = makeNewEffect()
348 ..indexableLength = makeNewEffect();
349 push(node.handler);
350 return node.body;
351 }
352
353 Expression traverseContinuation(Continuation cont) {
354 Continuation oldLoopHeader = currentLoopHeader;
355 currentLoopHeader = loopHierarchy.getLoopHeader(cont);
356 pushAction(() {
357 currentLoopHeader = oldLoopHeader;
358 });
359 for (Parameter param in cont.parameters) {
360 loopHeaderFor[param] = currentLoopHeader;
361 }
362 if (cont.isRecursive) {
363 addSideEffects(loopEffects.getSideEffectsInLoop(cont), length: false);
364 if (loopEffects.loopChangesLength(cont)) {
365 effectNumbers.indexableLength = makeNewEffect();
366 }
367 pushAction(() {
368 List<int> hoistedBindings = loopHoistedBindings[cont];
369 if (hoistedBindings != null) {
370 hoistedBindings.forEach(environment.remove);
371 }
372 });
373 } else {
374 EffectNumbers join = effectsAt[cont];
375 if (join != null) {
376 effectNumbers = join;
377 } else {
378 // This is a call continuation seen immediately after its use.
379 // Reuse the current effect numbers.
380 }
381 }
382
383 return cont.body;
384 }
385
386 void visitInvokeContinuation(InvokeContinuation node) {
387 Continuation cont = node.continuation.definition;
388 if (cont.isRecursive) return;
389 EffectNumbers join = effectsAt[cont];
390 if (join == null) {
391 effectsAt[cont] = effectNumbers.copy();
392 } else {
393 if (effectNumbers.instanceField != join.instanceField) {
394 join.instanceField = makeNewEffect();
395 }
396 if (effectNumbers.staticField != join.staticField) {
397 join.staticField = makeNewEffect();
398 }
399 if (effectNumbers.indexableContent != join.indexableContent) {
400 join.indexableContent = makeNewEffect();
401 }
402 if (effectNumbers.indexableLength != join.indexableLength) {
403 join.indexableLength = makeNewEffect();
404 }
405 }
406 }
407
408 void visitBranch(Branch node) {
409 Continuation trueCont = node.trueContinuation.definition;
410 Continuation falseCont = node.falseContinuation.definition;
411 // Copy the effect number vector once, so the analysis of one branch does
412 // not influence the other.
413 effectsAt[trueCont] = effectNumbers;
414 effectsAt[falseCont] = effectNumbers.copy();
415 }
416
417 void visitInvokeMethod(InvokeMethod node) {
418 addSideEffects(world.getSideEffectsOfSelector(node.selector, node.mask));
419 }
420
421 void visitInvokeStatic(InvokeStatic node) {
422 addSideEffects(world.getSideEffectsOfElement(node.target));
423 }
424
425 void visitInvokeMethodDirectly(InvokeMethodDirectly node) {
426 FunctionElement target = node.target;
427 if (target is ConstructorBodyElement) {
428 ConstructorBodyElement body = target;
429 target = body.constructor;
sra1 2015/11/17 05:41:14 should be able to just do target = target.cons
asgerf 2015/11/17 12:43:42 Type promotion bails out if the block contains an
430 }
431 addSideEffects(world.getSideEffectsOfElement(target));
432 }
433
434 void visitInvokeConstructor(InvokeConstructor node) {
435 addSideEffects(world.getSideEffectsOfElement(node.target));
436 }
437
438 void visitSetStatic(SetStatic node) {
439 effectNumbers.staticField = makeNewEffect();
440 }
441
442 void visitSetField(SetField node) {
443 effectNumbers.instanceField = makeNewEffect();
444 }
445
446 void visitSetIndex(SetIndex node) {
447 effectNumbers.indexableContent = makeNewEffect();
448 }
449
450 void visitForeignCode(ForeignCode node) {
451 addSideEffects(node.nativeBehavior.sideEffects);
452 }
453
454 void visitGetLazyStatic(GetLazyStatic node) {
455 // TODO(asgerf): How do we get the side effects of a lazy field initializer?
456 addAllSideEffects();
457 }
458
459 void visitAwait(Await node) {
460 addAllSideEffects();
461 }
462
463 void visitYield(Yield node) {
464 addAllSideEffects();
465 }
466
467 void visitApplyBuiltinMethod(ApplyBuiltinMethod node) {
468 // Push and pop.
469 effectNumbers.indexableContent = makeNewEffect();
470 effectNumbers.indexableLength = makeNewEffect();
471 }
472 }
473
474 /// For each of the four categories of heap locations, the IR is divided into
475 /// regions wherein the given heap locations are known not to be modified.
476 ///
477 /// Each region is identified by its "effect number". Effect numbers from
478 /// different categories have no relationship to each other.
479 class EffectNumbers {
480 int indexableLength = 0;
481 int indexableContent = 0;
482 int staticField = 0;
483 int instanceField = 0;
484
485 EffectNumbers copy() {
486 return new EffectNumbers()
487 ..indexableLength = indexableLength
488 ..indexableContent = indexableContent
489 ..staticField = staticField
490 ..instanceField = instanceField;
491 }
492 }
493
494 /// Maps vectors to numbers, such that two vectors with the same contents
495 /// map to the same number.
496 class GvnTable {
497 Map<GvnEntry, int> _table = <GvnEntry, int>{};
498 int _usedGvns = 0;
499 int _makeNewGvn() => ++_usedGvns;
500
501 int insert(List vector) {
502 return _table.putIfAbsent(new GvnEntry(vector), _makeNewGvn);
503 }
504 }
505
506 /// Wrapper around a [List] that compares for equality based on contents
507 /// instead of object identity.
508 class GvnEntry {
509 final List vector;
510 final int hashCode;
511
512 GvnEntry(List vector) : vector = vector, hashCode = computeHashCode(vector);
513
514 bool operator==(other) {
515 if (other is! GvnEntry) return false;
516 GvnEntry entry = other;
517 List otherVector = entry.vector;
518 if (vector.length != otherVector.length) return false;
519 for (int i = 0; i < vector.length; ++i) {
520 if (vector[i] != otherVector[i]) return false;
521 }
522 return true;
523 }
524
525 /// Combines the hash codes of [vector] using Jenkin's hash function.
sra1 2015/11/17 05:41:14 At least say 'modified' Jenkin's, since the Smi ma
asgerf 2015/11/17 12:43:42 Done.
526 static int computeHashCode(List vector) {
527 int hash = 0;
528 for (int i = 0; i < vector.length; ++i) {
529 hash = 0x1fffffff & (hash + vector[i].hashCode);
sra1 2015/11/17 05:41:14 Do all the things we put in the vector have fast h
asgerf 2015/11/17 12:43:42 There are: Element, DartType, Primitive, BuiltinOp
530 hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
531 hash = hash ^ (hash >> 6);
532 }
533 hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
534 hash = hash ^ (hash >> 11);
535 return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
536 }
537 }
538
539 /// Converts GVN'able primitives to a vector containing all the values
540 /// to be considered when computing a GVN for it.
541 ///
542 /// This includes the instruction type, inputs, effect numbers for any part
543 /// of the heap being depended on, as well as any instruction-specific payload
544 /// such as any DartTypes, Elements, and operator kinds.
545 ///
546 /// Each `visit` or `process` method for a primitive must initialize [vector]
547 /// if the primitive is GVN'able and fill in any components except the inputs.
548 /// The inputs will be filled in by [processReference].
549 class GvnVectorBuilder extends DeepRecursiveVisitor {
550 List vector;
551 final Map<Primitive, int> gvnFor;
552 final JavaScriptBackend backend;
553 EffectNumbers effectNumbers;
554
555 GvnVectorBuilder(this.gvnFor, this.backend);
556
557 List make(Primitive prim, EffectNumbers effectNumbers) {
558 this.effectNumbers = effectNumbers;
559 vector = null;
560 visit(prim);
561 return vector;
562 }
563
564 /// The `process` methods below do not insert the referenced arguments into
565 /// the vector, but instead rely on them being inserted here.
566 processReference(Reference ref) {
567 if (vector == null) return;
568 Primitive prim = ref.definition.effectiveDefinition;
569 vector.add(gvnFor[prim] ?? prim);
570 }
571
572 visitTypeTest(TypeTest node) {
573 vector = [GvnCode.TYPE_TEST, node.dartType];
574 processReference(node.value);
575 node.typeArguments.forEach(processReference);
576 // Suppress processing of the interceptor argument.
577 }
578
579 processTypeTestViaFlag(TypeTestViaFlag node) {
580 vector = [GvnCode.TYPE_TEST_VIA_FLAG, node.dartType];
581 }
582
583 processApplyBuiltinOperator(ApplyBuiltinOperator node) {
584 vector = [GvnCode.BUILTIN_OPERATOR, node.operator];
585 }
586
587 processGetLength(GetLength node) {
588 // TODO(asgerf): Take fixed lengths into account?
sra1 2015/11/17 05:41:14 Yes. For typed arrays V8 should know how to connec
asgerf 2015/11/17 12:43:42 Acknowledged.
589 vector = [GvnCode.GET_LENGTH, effectNumbers.indexableLength];
590 }
591
592 bool isImmutable(Element element) {
593 return element.isFunction ||
594 element.isField && (element.isFinal || element.isConst);
595 }
596
597 bool isNativeField(FieldElement field) {
598 // TODO(asgerf): We should add a GetNativeField instruction.
sra1 2015/11/17 05:41:14 Maybe called GetProperty
asgerf 2015/11/17 12:43:42 Acknowledged.
599 return backend.isNative(field);
600 }
601
602 processGetField(GetField node) {
603 if (isNativeField(node.field)) {
604 vector = null; // Native field access cannot be GVN'ed.
605 } else if (isImmutable(node.field)) {
606 vector = [GvnCode.GET_FIELD, node.field];
607 } else {
608 vector = [GvnCode.GET_FIELD, node.field, effectNumbers.instanceField];
609 }
610 }
611
612 processGetIndex(GetIndex node) {
613 vector = [GvnCode.GET_INDEX, effectNumbers.indexableContent];
614 }
615
616 processGetStatic(GetStatic node) {
617 if (isImmutable(node.element)) {
618 vector = [GvnCode.GET_STATIC, node.element];
619 } else {
620 vector = [GvnCode.GET_STATIC, node.element, effectNumbers.staticField];
621 }
622 }
623
624 processConstant(Constant node) {
625 vector = [GvnCode.CONSTANT, node.value];
626 }
627
628 processReifyRuntimeType(ReifyRuntimeType node) {
629 vector = [GvnCode.REIFY_RUNTIME_TYPE];
630 }
631
632 processReadTypeVariable(ReadTypeVariable node) {
633 vector = [GvnCode.READ_TYPE_VARIABLE, node.variable];
634 }
635
636 processTypeExpression(TypeExpression node) {
637 vector = [GvnCode.TYPE_EXPRESSION, node.dartType];
638 }
639
640 processInterceptor(Interceptor node) {
641 vector = [GvnCode.INTERCEPTOR];
642 }
643 }
644
645 class GvnCode {
646 static const int TYPE_TEST = 1;
647 static const int TYPE_TEST_VIA_FLAG = 2;
648 static const int BUILTIN_OPERATOR = 3;
649 static const int GET_LENGTH = 4;
650 static const int GET_FIELD = 5;
651 static const int GET_INDEX = 6;
652 static const int GET_STATIC = 7;
653 static const int CONSTANT = 8;
654 static const int REIFY_RUNTIME_TYPE = 9;
655 static const int READ_TYPE_VARIABLE = 10;
656 static const int TYPE_EXPRESSION = 11;
657 static const int INTERCEPTOR = 12;
658 }
659
660 typedef ReferenceCallback(Reference ref);
661 class ReferenceVisitor extends DeepRecursiveVisitor {
sra1 2015/11/17 05:41:13 Maybe we should split DeepRecursiveVisitor into th
asgerf 2015/11/17 12:43:42 The visitors need an overhaul, but I'd rather wait
662 ReferenceCallback callback;
663
664 ReferenceVisitor(this.callback);
665
666 @override
667 processReference(Reference ref) {
668 callback(ref);
669 }
670
671 static void forEachReference(Node node, ReferenceCallback callback) {
sra1 2015/11/17 05:41:13 This is a pretty wild visitor pattern if `node` is
asgerf 2015/11/17 12:43:42 Done.
672 new ReferenceVisitor(callback).visit(node);
673 }
674 }
OLDNEW
« no previous file with comments | « no previous file | pkg/compiler/lib/src/cps_ir/loop_effects.dart » ('j') | pkg/compiler/lib/src/cps_ir/loop_effects.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698