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