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

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

Powered by Google App Engine
This is Rietveld 408576698