| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 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 | 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 library dart2js.cps_ir.gvn; | 5 library dart2js.cps_ir.gvn; |
| 6 | 6 |
| 7 import 'cps_ir_nodes.dart'; | 7 import 'cps_ir_nodes.dart'; |
| 8 import '../universe/side_effects.dart'; | 8 import '../universe/side_effects.dart'; |
| 9 import '../elements/elements.dart'; | 9 import '../elements/elements.dart'; |
| 10 import 'optimizers.dart' show Pass; | 10 import 'optimizers.dart' show Pass; |
| 11 import 'loop_hierarchy.dart'; | 11 import 'loop_hierarchy.dart'; |
| 12 import 'loop_effects.dart'; | 12 import 'loop_effects.dart'; |
| 13 import '../world.dart'; | 13 import '../world.dart'; |
| 14 import '../compiler.dart' show Compiler; | 14 import '../compiler.dart' show Compiler; |
| 15 import '../js_backend/js_backend.dart' show JavaScriptBackend; | 15 import '../js_backend/js_backend.dart' show JavaScriptBackend; |
| 16 import '../constants/values.dart'; | 16 import '../constants/values.dart'; |
| 17 import 'type_mask_system.dart'; | 17 import 'type_mask_system.dart'; |
| 18 import 'effects.dart'; |
| 18 | 19 |
| 19 /// Eliminates redundant primitives by reusing the value of another primitive | 20 /// Eliminates redundant primitives by reusing the value of another primitive |
| 20 /// that is known to have the same result. Primitives are also hoisted out of | 21 /// that is known to have the same result. Primitives are also hoisted out of |
| 21 /// loops when possible. | 22 /// loops when possible. |
| 22 /// | 23 /// |
| 23 /// Reusing values can introduce new temporaries, which in some cases is more | 24 /// Reusing values can introduce new temporaries, which in some cases is more |
| 24 /// expensive than recomputing the value on-demand. For example, pulling an | 25 /// expensive than recomputing the value on-demand. For example, pulling an |
| 25 /// expression such as "n+1" out of a loop is generally not worth it. | 26 /// expression such as "n+1" out of a loop is generally not worth it. |
| 26 /// Such primitives are said to be "trivial". | 27 /// Such primitives are said to be "trivial". |
| 27 /// | 28 /// |
| 28 /// Trivial primitives are shared on-demand, i.e. they are only shared if | 29 /// Trivial primitives are shared on-demand, i.e. they are only shared if |
| 29 /// this enables a non-trivial primitive to be hoisted out of a loop. | 30 /// this enables a non-trivial primitive to be hoisted out of a loop. |
| 30 // | 31 // |
| 31 // TODO(asgerf): Enable hoisting across refinement guards when this is safe: | 32 // TODO(asgerf): Enable hoisting across refinement guards when this is safe: |
| 32 // - Determine the type required for a given primitive to be "safe" | 33 // - Determine the type required for a given primitive to be "safe" |
| 33 // - Recompute the type of a primitive after hoisting. | 34 // - Recompute the type of a primitive after hoisting. |
| 34 // E.g. GetIndex on a String can become a GetIndex on an arbitrary | 35 // E.g. GetIndex on a String can become a GetIndex on an arbitrary |
| 35 // indexable, which is still safe but the type may change | 36 // indexable, which is still safe but the type may change |
| 36 // - Since the new type may be worse, insert a refinement at the old | 37 // - Since the new type may be worse, insert a refinement at the old |
| 37 // definition site, so we do not degrade existing type information. | 38 // definition site, so we do not degrade existing type information. |
| 38 // | 39 // |
| 39 // TODO(asgerf): Put this pass at a better place in the pipeline. We currently | |
| 40 // cannot put it anywhere we want, because this pass relies on refinement | |
| 41 // nodes being present (for safety), whereas other passes rely on refinement | |
| 42 // nodes being absent (for simplicity & precision). | |
| 43 // | |
| 44 class GVN extends TrampolineRecursiveVisitor implements Pass { | 40 class GVN extends TrampolineRecursiveVisitor implements Pass { |
| 45 String get passName => 'GVN'; | 41 String get passName => 'GVN'; |
| 46 | 42 |
| 47 final Compiler compiler; | 43 final Compiler compiler; |
| 48 final TypeMaskSystem types; | 44 final TypeMaskSystem types; |
| 49 JavaScriptBackend get backend => compiler.backend; | 45 JavaScriptBackend get backend => compiler.backend; |
| 50 World get world => compiler.world; | 46 World get world => compiler.world; |
| 51 | 47 |
| 52 final GvnTable gvnTable = new GvnTable(); | 48 final GvnTable gvnTable = new GvnTable(); |
| 53 GvnVectorBuilder gvnVectorBuilder; | 49 GvnVectorBuilder gvnVectorBuilder; |
| 54 LoopHierarchy loopHierarchy; | 50 LoopHierarchy loopHierarchy; |
| 55 LoopSideEffects loopEffects; | 51 LoopSideEffects loopEffects; |
| 56 | 52 |
| 53 final EffectNumberer effectNumberer = new EffectNumberer(); |
| 54 |
| 57 /// Effect numbers at the given join point. | 55 /// Effect numbers at the given join point. |
| 58 Map<Continuation, EffectNumbers> effectsAt = <Continuation, EffectNumbers>{}; | 56 Map<Continuation, EffectNumbers> effectsAt = <Continuation, EffectNumbers>{}; |
| 59 | 57 |
| 60 /// The effect numbers at the current position (during traversal). | 58 /// The effect numbers at the current position (during traversal). |
| 61 EffectNumbers effectNumbers = new EffectNumbers(); | 59 EffectNumbers effectNumbers; |
| 62 | 60 |
| 63 /// The loop currently enclosing the binding of a given primitive. | 61 /// The loop currently enclosing the binding of a given primitive. |
| 64 final Map<Primitive, Continuation> loopHeaderFor = | 62 final Map<Primitive, Continuation> loopHeaderFor = |
| 65 <Primitive, Continuation>{}; | 63 <Primitive, Continuation>{}; |
| 66 | 64 |
| 67 /// The GVNs for primitives that have been hoisted outside the given loop. | 65 /// The GVNs for primitives that have been hoisted outside the given loop. |
| 68 /// | 66 /// |
| 69 /// These should be removed from the environment when exiting the loop. | 67 /// These should be removed from the environment when exiting the loop. |
| 70 final Map<Continuation, List<int>> loopHoistedBindings = | 68 final Map<Continuation, List<int>> loopHoistedBindings = |
| 71 <Continuation, List<int>>{}; | 69 <Continuation, List<int>>{}; |
| 72 | 70 |
| 73 /// Maps GVNs to a currently-in-scope binding for that value. | 71 /// Maps GVNs to a currently-in-scope binding for that value. |
| 74 final Map<int, Primitive> environment = <int, Primitive>{}; | 72 final Map<int, Primitive> environment = <int, Primitive>{}; |
| 75 | 73 |
| 76 /// Maps GVN'able primitives to their global value number. | 74 /// Maps GVN'able primitives to their global value number. |
| 77 final Map<Primitive, int> gvnFor = <Primitive, int>{}; | 75 final Map<Primitive, int> gvnFor = <Primitive, int>{}; |
| 78 | 76 |
| 79 Continuation currentLoopHeader; | 77 Continuation currentLoopHeader; |
| 80 | 78 |
| 81 GVN(this.compiler, this.types); | 79 GVN(this.compiler, this.types); |
| 82 | 80 |
| 83 int _usedEffectNumbers = 0; | |
| 84 int makeNewEffect() => ++_usedEffectNumbers; | |
| 85 | |
| 86 void rewrite(FunctionDefinition node) { | 81 void rewrite(FunctionDefinition node) { |
| 82 effectNumbers = new EffectNumbers.fresh(effectNumberer); |
| 87 gvnVectorBuilder = new GvnVectorBuilder(gvnFor, compiler, types); | 83 gvnVectorBuilder = new GvnVectorBuilder(gvnFor, compiler, types); |
| 88 loopHierarchy = new LoopHierarchy(node); | 84 loopHierarchy = new LoopHierarchy(node); |
| 89 loopEffects = | 85 loopEffects = |
| 90 new LoopSideEffects(node, world, loopHierarchy: loopHierarchy); | 86 new LoopSideEffects(node, world, loopHierarchy: loopHierarchy); |
| 91 visit(node); | 87 visit(node); |
| 92 } | 88 } |
| 93 | 89 |
| 94 // ------------------ GLOBAL VALUE NUMBERING --------------------- | 90 // ------------------ GLOBAL VALUE NUMBERING --------------------- |
| 95 | 91 |
| 96 /// True if [prim] can be eliminated if its value is already in scope. | 92 /// True if [prim] can be eliminated if its value is already in scope. |
| (...skipping 21 matching lines...) Expand all Loading... |
| 118 // Do not share refinements (they have no runtime or code size cost), and | 114 // Do not share refinements (they have no runtime or code size cost), and |
| 119 // do not put them in the GVN table because GvnVectorBuilder unfolds | 115 // do not put them in the GVN table because GvnVectorBuilder unfolds |
| 120 // refinements by itself. | 116 // refinements by itself. |
| 121 return next; | 117 return next; |
| 122 } | 118 } |
| 123 | 119 |
| 124 // Update effect numbers due to side effects from a static initializer. | 120 // Update effect numbers due to side effects from a static initializer. |
| 125 // GetLazyStatic is GVN'ed like a GetStatic, but the effects of the static | 121 // GetLazyStatic is GVN'ed like a GetStatic, but the effects of the static |
| 126 // initializer occur before reading the field. | 122 // initializer occur before reading the field. |
| 127 if (prim is GetLazyStatic) { | 123 if (prim is GetLazyStatic) { |
| 128 visit(prim); | 124 addSideEffectsOfPrimitive(prim); |
| 129 } | 125 } |
| 130 | 126 |
| 131 // Compute the GVN vector for this computation. | 127 // Compute the GVN vector for this computation. |
| 132 List vector = gvnVectorBuilder.make(prim, effectNumbers); | 128 List vector = gvnVectorBuilder.make(prim, effectNumbers); |
| 133 | 129 |
| 134 // Update effect numbers due to side effects. | 130 // Update effect numbers due to side effects. |
| 135 // Do this after computing the GVN vector so the primitive's GVN is not | 131 // Do this after computing the GVN vector so the primitive's GVN is not |
| 136 // influenced by its own side effects, except in the case of GetLazyStatic. | 132 // influenced by its own side effects, except in the case of GetLazyStatic. |
| 137 if (prim is! GetLazyStatic) { | 133 if (prim is! GetLazyStatic) { |
| 138 visit(prim); | 134 addSideEffectsOfPrimitive(prim); |
| 139 } | 135 } |
| 140 | 136 |
| 141 if (vector == null) { | 137 if (vector == null) { |
| 142 // The primitive is not GVN'able. Move on. | 138 // The primitive is not GVN'able. Move on. |
| 143 return next; | 139 return next; |
| 144 } | 140 } |
| 145 | 141 |
| 146 // Compute the GVN for this primitive. | 142 // Compute the GVN for this primitive. |
| 147 int gvn = gvnTable.insert(vector); | 143 int gvn = gvnTable.insert(vector); |
| 148 gvnFor[prim] = gvn; | 144 gvnFor[prim] = gvn; |
| (...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 340 | 336 |
| 341 bool canIgnoreRefinementGuards(Primitive primitive) { | 337 bool canIgnoreRefinementGuards(Primitive primitive) { |
| 342 return primitive is Interceptor; | 338 return primitive is Interceptor; |
| 343 } | 339 } |
| 344 | 340 |
| 345 /// Returns true if [prim] is a constant that has no significant runtime cost. | 341 /// Returns true if [prim] is a constant that has no significant runtime cost. |
| 346 bool isFastConstant(Primitive prim) { | 342 bool isFastConstant(Primitive prim) { |
| 347 return prim is Constant && (prim.value.isPrimitive || prim.value.isDummy); | 343 return prim is Constant && (prim.value.isPrimitive || prim.value.isDummy); |
| 348 } | 344 } |
| 349 | 345 |
| 350 /// True if [element] is a final or constant field or a function. | |
| 351 bool isImmutable(Element element) { | |
| 352 if (element.isField && backend.isNative(element)) return false; | |
| 353 return element.isField && world.fieldNeverChanges(element) || | |
| 354 element.isFunction; | |
| 355 } | |
| 356 | |
| 357 bool isImmutableLength(GetLength length) { | |
| 358 return types.isDefinitelyFixedLengthIndexable(length.object.definition.type, | |
| 359 allowNull: true); | |
| 360 } | |
| 361 | |
| 362 /// Assuming [prim] has no side effects, returns true if it can safely | 346 /// Assuming [prim] has no side effects, returns true if it can safely |
| 363 /// be hoisted out of [loop] without changing its value or changing the timing | 347 /// be hoisted out of [loop] without changing its value or changing the timing |
| 364 /// of a thrown exception. | 348 /// of a thrown exception. |
| 365 bool canHoistHeapDependencyOutOfLoop(Primitive prim, Continuation loop) { | 349 bool canHoistHeapDependencyOutOfLoop(Primitive prim, Continuation loop) { |
| 366 // If the primitive might throw, we have to check that it is the first | 350 // If the primitive might throw, we have to check that it is the first |
| 367 // impure expression in the loop. This has already been checked if | 351 // impure expression in the loop. This has already been checked if |
| 368 // [loop] is the current loop header, but for other loops we just give up. | 352 // [loop] is the current loop header, but for other loops we just give up. |
| 369 if (!prim.isSafeForElimination && loop != currentLoopHeader) { | 353 if (!prim.isSafeForElimination && loop != currentLoopHeader) { |
| 370 return false; | 354 return false; |
| 371 } | 355 } |
| 372 if (prim is GetLength && !isImmutableLength(prim)) { | 356 int effects = loopEffects.getSideEffectsInLoop(loop); |
| 373 return !loopEffects.loopChangesLength(loop); | 357 return Effects.changesToDepends(effects) & prim.effects == 0; |
| 374 } else if (prim is GetField && !isImmutable(prim.field)) { | |
| 375 return !loopEffects.getSideEffectsInLoop(loop).changesInstanceProperty(); | |
| 376 } else if (prim is GetStatic && !isImmutable(prim.element)) { | |
| 377 return !loopEffects.getSideEffectsInLoop(loop).changesStaticProperty(); | |
| 378 } else if (prim is GetIndex) { | |
| 379 return !loopEffects.getSideEffectsInLoop(loop).changesIndex(); | |
| 380 } else { | |
| 381 return true; | |
| 382 } | |
| 383 } | 358 } |
| 384 | 359 |
| 385 // ------------------ TRAVERSAL AND EFFECT NUMBERING --------------------- | 360 // ------------------ TRAVERSAL AND EFFECT NUMBERING --------------------- |
| 386 // | 361 // |
| 387 // These methods traverse the IR while updating the current effect numbers. | 362 // These methods traverse the IR while updating the current effect numbers. |
| 388 // They are not specific to GVN. | 363 // They are not specific to GVN. |
| 389 // | |
| 390 // TODO(asgerf): Avoid duplicated code for side effect analysis. | |
| 391 // Should be easier to fix once primitives and call expressions are the same. | |
| 392 | 364 |
| 393 void addSideEffects(SideEffects fx, {bool length: true}) { | 365 void addSideEffectsOfPrimitive(Primitive prim) { |
| 394 if (fx.changesInstanceProperty()) { | 366 addSideEffects(prim.effects); |
| 395 effectNumbers.instanceField = makeNewEffect(); | |
| 396 } | |
| 397 if (fx.changesStaticProperty()) { | |
| 398 effectNumbers.staticField = makeNewEffect(); | |
| 399 } | |
| 400 if (fx.changesIndex()) { | |
| 401 effectNumbers.indexableContent = makeNewEffect(); | |
| 402 } | |
| 403 if (length && fx.changesIndex()) { | |
| 404 effectNumbers.indexableLength = makeNewEffect(); | |
| 405 } | |
| 406 } | 367 } |
| 407 | 368 |
| 408 void addAllSideEffects() { | 369 void addSideEffects(int effectFlags) { |
| 409 effectNumbers.instanceField = makeNewEffect(); | 370 effectNumbers.change(effectNumberer, effectFlags); |
| 410 effectNumbers.staticField = makeNewEffect(); | |
| 411 effectNumbers.indexableContent = makeNewEffect(); | |
| 412 effectNumbers.indexableLength = makeNewEffect(); | |
| 413 } | 371 } |
| 414 | 372 |
| 415 Expression traverseLetHandler(LetHandler node) { | 373 Expression traverseLetHandler(LetHandler node) { |
| 416 // Assume any kind of side effects may occur in the try block. | 374 // Assume any kind of side effects may occur in the try block. |
| 417 effectsAt[node.handler] = new EffectNumbers() | 375 effectsAt[node.handler] = new EffectNumbers.fresh(effectNumberer); |
| 418 ..instanceField = makeNewEffect() | |
| 419 ..staticField = makeNewEffect() | |
| 420 ..indexableContent = makeNewEffect() | |
| 421 ..indexableLength = makeNewEffect(); | |
| 422 push(node.handler); | 376 push(node.handler); |
| 423 return node.body; | 377 return node.body; |
| 424 } | 378 } |
| 425 | 379 |
| 426 Expression traverseContinuation(Continuation cont) { | 380 Expression traverseContinuation(Continuation cont) { |
| 427 Continuation oldLoopHeader = currentLoopHeader; | 381 Continuation oldLoopHeader = currentLoopHeader; |
| 428 currentLoopHeader = loopHierarchy.getLoopHeader(cont); | 382 currentLoopHeader = loopHierarchy.getLoopHeader(cont); |
| 429 pushAction(() { | 383 pushAction(() { |
| 430 currentLoopHeader = oldLoopHeader; | 384 currentLoopHeader = oldLoopHeader; |
| 431 }); | 385 }); |
| 432 for (Parameter param in cont.parameters) { | 386 for (Parameter param in cont.parameters) { |
| 433 loopHeaderFor[param] = currentLoopHeader; | 387 loopHeaderFor[param] = currentLoopHeader; |
| 434 } | 388 } |
| 435 if (cont.isRecursive) { | 389 if (cont.isRecursive) { |
| 436 addSideEffects(loopEffects.getSideEffectsInLoop(cont), length: false); | 390 addSideEffects(loopEffects.getSideEffectsInLoop(cont)); |
| 437 if (loopEffects.loopChangesLength(cont)) { | |
| 438 effectNumbers.indexableLength = makeNewEffect(); | |
| 439 } | |
| 440 pushAction(() { | 391 pushAction(() { |
| 441 List<int> hoistedBindings = loopHoistedBindings[cont]; | 392 List<int> hoistedBindings = loopHoistedBindings[cont]; |
| 442 if (hoistedBindings != null) { | 393 if (hoistedBindings != null) { |
| 443 hoistedBindings.forEach(environment.remove); | 394 hoistedBindings.forEach(environment.remove); |
| 444 } | 395 } |
| 445 }); | 396 }); |
| 446 } else { | 397 } else { |
| 447 EffectNumbers join = effectsAt[cont]; | 398 effectNumbers = effectsAt[cont]; |
| 448 if (join != null) { | 399 assert(effectNumbers != null); |
| 449 effectNumbers = join; | |
| 450 } else { | |
| 451 // This is a call continuation seen immediately after its use. | |
| 452 // Reuse the current effect numbers. | |
| 453 } | |
| 454 } | 400 } |
| 455 | 401 |
| 456 return cont.body; | 402 return cont.body; |
| 457 } | 403 } |
| 458 | 404 |
| 459 void visitInvokeContinuation(InvokeContinuation node) { | 405 void visitInvokeContinuation(InvokeContinuation node) { |
| 460 Continuation cont = node.continuation.definition; | 406 Continuation cont = node.continuation.definition; |
| 461 if (cont.isRecursive) return; | 407 if (cont.isRecursive) return; |
| 462 EffectNumbers join = effectsAt[cont]; | 408 EffectNumbers join = effectsAt[cont]; |
| 463 if (join == null) { | 409 if (join == null) { |
| 464 effectsAt[cont] = effectNumbers.copy(); | 410 effectsAt[cont] = effectNumbers.copy(); |
| 465 } else { | 411 } else { |
| 466 if (effectNumbers.instanceField != join.instanceField) { | 412 join.join(effectNumberer, effectNumbers); |
| 467 join.instanceField = makeNewEffect(); | |
| 468 } | |
| 469 if (effectNumbers.staticField != join.staticField) { | |
| 470 join.staticField = makeNewEffect(); | |
| 471 } | |
| 472 if (effectNumbers.indexableContent != join.indexableContent) { | |
| 473 join.indexableContent = makeNewEffect(); | |
| 474 } | |
| 475 if (effectNumbers.indexableLength != join.indexableLength) { | |
| 476 join.indexableLength = makeNewEffect(); | |
| 477 } | |
| 478 } | 413 } |
| 479 } | 414 } |
| 480 | 415 |
| 481 void visitBranch(Branch node) { | 416 void visitBranch(Branch node) { |
| 482 Continuation trueCont = node.trueContinuation.definition; | 417 Continuation trueCont = node.trueContinuation.definition; |
| 483 Continuation falseCont = node.falseContinuation.definition; | 418 Continuation falseCont = node.falseContinuation.definition; |
| 484 // Copy the effect number vector once, so the analysis of one branch does | 419 // Copy the effect number vector once, so the analysis of one branch does |
| 485 // not influence the other. | 420 // not influence the other. |
| 486 effectsAt[trueCont] = effectNumbers; | 421 effectsAt[trueCont] = effectNumbers; |
| 487 effectsAt[falseCont] = effectNumbers.copy(); | 422 effectsAt[falseCont] = effectNumbers.copy(); |
| 488 } | 423 } |
| 489 | |
| 490 void visitInvokeMethod(InvokeMethod node) { | |
| 491 addSideEffects(world.getSideEffectsOfSelector(node.selector, node.mask)); | |
| 492 } | |
| 493 | |
| 494 void visitInvokeStatic(InvokeStatic node) { | |
| 495 addSideEffects(world.getSideEffectsOfElement(node.target)); | |
| 496 } | |
| 497 | |
| 498 void visitInvokeMethodDirectly(InvokeMethodDirectly node) { | |
| 499 FunctionElement target = node.target; | |
| 500 if (target is ConstructorBodyElement) { | |
| 501 ConstructorBodyElement body = target; | |
| 502 target = body.constructor; | |
| 503 } | |
| 504 addSideEffects(world.getSideEffectsOfElement(target)); | |
| 505 } | |
| 506 | |
| 507 void visitInvokeConstructor(InvokeConstructor node) { | |
| 508 addSideEffects(world.getSideEffectsOfElement(node.target)); | |
| 509 } | |
| 510 | |
| 511 void visitSetStatic(SetStatic node) { | |
| 512 effectNumbers.staticField = makeNewEffect(); | |
| 513 } | |
| 514 | |
| 515 void visitSetField(SetField node) { | |
| 516 effectNumbers.instanceField = makeNewEffect(); | |
| 517 } | |
| 518 | |
| 519 void visitSetIndex(SetIndex node) { | |
| 520 effectNumbers.indexableContent = makeNewEffect(); | |
| 521 } | |
| 522 | |
| 523 void visitForeignCode(ForeignCode node) { | |
| 524 addSideEffects(node.nativeBehavior.sideEffects); | |
| 525 } | |
| 526 | |
| 527 void visitGetLazyStatic(GetLazyStatic node) { | |
| 528 // TODO(asgerf): How do we get the side effects of a lazy field initializer? | |
| 529 addAllSideEffects(); | |
| 530 } | |
| 531 | |
| 532 void visitAwait(Await node) { | |
| 533 addAllSideEffects(); | |
| 534 } | |
| 535 | |
| 536 void visitYield(Yield node) { | |
| 537 addAllSideEffects(); | |
| 538 } | |
| 539 | |
| 540 void visitApplyBuiltinMethod(ApplyBuiltinMethod node) { | |
| 541 // Push and pop. | |
| 542 effectNumbers.indexableContent = makeNewEffect(); | |
| 543 effectNumbers.indexableLength = makeNewEffect(); | |
| 544 } | |
| 545 } | |
| 546 | |
| 547 /// For each of the four categories of heap locations, the IR is divided into | |
| 548 /// regions wherein the given heap locations are known not to be modified. | |
| 549 /// | |
| 550 /// Each region is identified by its "effect number". Effect numbers from | |
| 551 /// different categories have no relationship to each other. | |
| 552 class EffectNumbers { | |
| 553 int indexableLength = 0; | |
| 554 int indexableContent = 0; | |
| 555 int staticField = 0; | |
| 556 int instanceField = 0; | |
| 557 | |
| 558 EffectNumbers copy() { | |
| 559 return new EffectNumbers() | |
| 560 ..indexableLength = indexableLength | |
| 561 ..indexableContent = indexableContent | |
| 562 ..staticField = staticField | |
| 563 ..instanceField = instanceField; | |
| 564 } | |
| 565 } | 424 } |
| 566 | 425 |
| 567 /// Maps vectors to numbers, such that two vectors with the same contents | 426 /// Maps vectors to numbers, such that two vectors with the same contents |
| 568 /// map to the same number. | 427 /// map to the same number. |
| 569 class GvnTable { | 428 class GvnTable { |
| 570 Map<GvnEntry, int> _table = <GvnEntry, int>{}; | 429 Map<GvnEntry, int> _table = <GvnEntry, int>{}; |
| 571 int _usedGvns = 0; | 430 int _usedGvns = 0; |
| 572 int _makeNewGvn() => ++_usedGvns; | 431 int _makeNewGvn() => ++_usedGvns; |
| 573 | 432 |
| 574 int insert(List vector) { | 433 int insert(List vector) { |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 652 | 511 |
| 653 processTypeTestViaFlag(TypeTestViaFlag node) { | 512 processTypeTestViaFlag(TypeTestViaFlag node) { |
| 654 vector = [GvnCode.TYPE_TEST_VIA_FLAG, node.dartType]; | 513 vector = [GvnCode.TYPE_TEST_VIA_FLAG, node.dartType]; |
| 655 } | 514 } |
| 656 | 515 |
| 657 processApplyBuiltinOperator(ApplyBuiltinOperator node) { | 516 processApplyBuiltinOperator(ApplyBuiltinOperator node) { |
| 658 vector = [GvnCode.BUILTIN_OPERATOR, node.operator.index]; | 517 vector = [GvnCode.BUILTIN_OPERATOR, node.operator.index]; |
| 659 } | 518 } |
| 660 | 519 |
| 661 processGetLength(GetLength node) { | 520 processGetLength(GetLength node) { |
| 662 if (isImmutableLength(node)) { | 521 if (node.isFinal) { |
| 663 // Omit the effect number for fixed-length lists. Note that if a the list | 522 // Omit the effect number for fixed-length lists. Note that if a the list |
| 664 // gets refined to a fixed-length type, we still won't be able to GVN a | 523 // gets refined to a fixed-length type, we still won't be able to GVN a |
| 665 // GetLength across the refinement, because the first GetLength uses an | 524 // GetLength across the refinement, because the first GetLength uses an |
| 666 // effect number in its vector while the second one does not. | 525 // effect number in its vector while the second one does not. |
| 667 vector = [GvnCode.GET_LENGTH]; | 526 vector = [GvnCode.GET_LENGTH]; |
| 668 } else { | 527 } else { |
| 669 vector = [GvnCode.GET_LENGTH, effectNumbers.indexableLength]; | 528 vector = [GvnCode.GET_LENGTH, effectNumbers.indexableLength]; |
| 670 } | 529 } |
| 671 } | 530 } |
| 672 | 531 |
| 673 bool isImmutable(Element element) { | |
| 674 return element.isFunction || | |
| 675 element.isField && world.fieldNeverChanges(element); | |
| 676 } | |
| 677 | |
| 678 bool isImmutableLength(GetLength length) { | |
| 679 return types.isDefinitelyFixedLengthIndexable(length.object.definition.type, | |
| 680 allowNull: true); | |
| 681 } | |
| 682 | |
| 683 bool isNativeField(FieldElement field) { | 532 bool isNativeField(FieldElement field) { |
| 684 // TODO(asgerf): We should add a GetNativeField instruction. | 533 // TODO(asgerf): We should add a GetNativeField instruction. |
| 685 return backend.isNative(field); | 534 return backend.isNative(field); |
| 686 } | 535 } |
| 687 | 536 |
| 688 processGetField(GetField node) { | 537 processGetField(GetField node) { |
| 689 if (isNativeField(node.field)) { | 538 if (isNativeField(node.field)) { |
| 690 vector = null; // Native field access cannot be GVN'ed. | 539 vector = null; // Native field access cannot be GVN'ed. |
| 691 } else if (isImmutable(node.field)) { | 540 } else if (node.isFinal) { |
| 692 vector = [GvnCode.GET_FIELD, node.field]; | 541 vector = [GvnCode.GET_FIELD, node.field]; |
| 693 } else { | 542 } else { |
| 694 vector = [GvnCode.GET_FIELD, node.field, effectNumbers.instanceField]; | 543 vector = [GvnCode.GET_FIELD, node.field, effectNumbers.instanceField]; |
| 695 } | 544 } |
| 696 } | 545 } |
| 697 | 546 |
| 698 processGetIndex(GetIndex node) { | 547 processGetIndex(GetIndex node) { |
| 699 vector = [GvnCode.GET_INDEX, effectNumbers.indexableContent]; | 548 vector = [GvnCode.GET_INDEX, effectNumbers.indexableContent]; |
| 700 } | 549 } |
| 701 | 550 |
| 702 visitGetStatic(GetStatic node) { | 551 visitGetStatic(GetStatic node) { |
| 703 if (isImmutable(node.element)) { | 552 if (node.isFinal) { |
| 704 vector = [GvnCode.GET_STATIC, node.element]; | 553 vector = [GvnCode.GET_STATIC, node.element]; |
| 705 } else { | 554 } else { |
| 706 vector = [GvnCode.GET_STATIC, node.element, effectNumbers.staticField]; | 555 vector = [GvnCode.GET_STATIC, node.element, effectNumbers.staticField]; |
| 707 } | 556 } |
| 708 // Suppress visit to witness argument. | 557 // Suppress visit to witness argument. |
| 709 } | 558 } |
| 710 | 559 |
| 711 processGetLazyStatic(GetLazyStatic node) { | 560 processGetLazyStatic(GetLazyStatic node) { |
| 712 if (isImmutable(node.element)) { | 561 if (node.isFinal) { |
| 713 vector = [GvnCode.GET_STATIC, node.element]; | 562 vector = [GvnCode.GET_STATIC, node.element]; |
| 714 } else { | 563 } else { |
| 715 vector = [GvnCode.GET_STATIC, node.element, effectNumbers.staticField]; | 564 vector = [GvnCode.GET_STATIC, node.element, effectNumbers.staticField]; |
| 716 } | 565 } |
| 717 } | 566 } |
| 718 | 567 |
| 719 processConstant(Constant node) { | 568 processConstant(Constant node) { |
| 720 vector = [GvnCode.CONSTANT, node.value]; | 569 vector = [GvnCode.CONSTANT, node.value]; |
| 721 } | 570 } |
| 722 | 571 |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 760 | 609 |
| 761 @override | 610 @override |
| 762 processReference(Reference ref) { | 611 processReference(Reference ref) { |
| 763 callback(ref); | 612 callback(ref); |
| 764 } | 613 } |
| 765 | 614 |
| 766 static void forEach(Primitive node, ReferenceCallback callback) { | 615 static void forEach(Primitive node, ReferenceCallback callback) { |
| 767 new InputVisitor(callback).visit(node); | 616 new InputVisitor(callback).visit(node); |
| 768 } | 617 } |
| 769 } | 618 } |
| OLD | NEW |