| 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; |
| (...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 169 // environment while processing the body of the LetPrim. | 169 // environment while processing the body of the LetPrim. |
| 170 environment[gvn] = prim; | 170 environment[gvn] = prim; |
| 171 pushAction(() { | 171 pushAction(() { |
| 172 assert(environment[gvn] == prim); | 172 assert(environment[gvn] == prim); |
| 173 environment[gvn] = existing; | 173 environment[gvn] = existing; |
| 174 }); | 174 }); |
| 175 | 175 |
| 176 return next; | 176 return next; |
| 177 } | 177 } |
| 178 | 178 |
| 179 bool isFirstImpureExpressionInLoop(Expression exp) { |
| 180 InteriorNode node = exp.parent; |
| 181 for (; node is Expression; node = node.parent) { |
| 182 if (node is LetPrim && node.primitive.isSafeForElimination) { |
| 183 continue; |
| 184 } |
| 185 if (node is LetCont) { |
| 186 continue; |
| 187 } |
| 188 return false; |
| 189 } |
| 190 return node == currentLoopHeader; |
| 191 } |
| 192 |
| 193 bool isHoistablePrimitive(Primitive prim) { |
| 194 if (prim.isSafeForElimination) return true; |
| 195 if (prim is NullCheck || |
| 196 prim is BoundsCheck || |
| 197 prim is GetLength || |
| 198 prim is GetField || |
| 199 prim is GetIndex) { |
| 200 // Expressions that potentially throw but have no other effects can be |
| 201 // hoisted if they occur as the first impure expression in a loop. |
| 202 // Note regarding BoundsCheck: the current array length is an input to |
| 203 // check, so the check itself has no heap dependency. It will only be |
| 204 // hoisted if the length was hoisted. |
| 205 // TODO(asgerf): In general we could hoist these out of multiple loops, |
| 206 // but the trick we use here only works for one loop level. |
| 207 return isFirstImpureExpressionInLoop(prim.parent); |
| 208 } |
| 209 return false; |
| 210 } |
| 211 |
| 179 /// Try to hoist the binding of [prim] out of loops. Returns `true` if it was | 212 /// Try to hoist the binding of [prim] out of loops. Returns `true` if it was |
| 180 /// hoisted or marked as a trivial hoist-on-demand primitive. | 213 /// hoisted or marked as a trivial hoist-on-demand primitive. |
| 181 bool tryToHoistOutOfLoop(Primitive prim, int gvn) { | 214 bool tryToHoistOutOfLoop(Primitive prim, int gvn) { |
| 182 // Do not hoist primitives with side effects. | |
| 183 if (!prim.isSafeForElimination) return false; | |
| 184 | |
| 185 // Bail out fast if the primitive is not inside a loop. | 215 // Bail out fast if the primitive is not inside a loop. |
| 186 if (currentLoopHeader == null) return false; | 216 if (currentLoopHeader == null) return false; |
| 187 | 217 |
| 218 // Do not hoist primitives with side effects. |
| 219 if (!isHoistablePrimitive(prim)) return false; |
| 220 |
| 188 LetPrim letPrim = prim.parent; | 221 LetPrim letPrim = prim.parent; |
| 189 | 222 |
| 190 // Find the depth of the outermost scope where we can bind the primitive | 223 // Find the depth of the outermost scope where we can bind the primitive |
| 191 // without bringing a reference out of scope. 0 is the depth of the | 224 // without bringing a reference out of scope. 0 is the depth of the |
| 192 // top-level scope. | 225 // top-level scope. |
| 193 int hoistDepth = 0; | 226 int hoistDepth = 0; |
| 194 List<Primitive> inputsHoistedOnDemand = <Primitive>[]; | 227 List<Primitive> inputsHoistedOnDemand = <Primitive>[]; |
| 195 InputVisitor.forEach(prim, (Reference ref) { | 228 InputVisitor.forEach(prim, (Reference ref) { |
| 196 Primitive input = ref.definition; | 229 Primitive input = ref.definition; |
| 197 if (canIgnoreRefinementGuards(prim)) { | 230 if (canIgnoreRefinementGuards(prim)) { |
| (...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 320 return element.isField && world.fieldNeverChanges(element) || | 353 return element.isField && world.fieldNeverChanges(element) || |
| 321 element.isFunction; | 354 element.isFunction; |
| 322 } | 355 } |
| 323 | 356 |
| 324 bool isImmutableLength(GetLength length) { | 357 bool isImmutableLength(GetLength length) { |
| 325 return types.isDefinitelyFixedLengthIndexable(length.object.definition.type, | 358 return types.isDefinitelyFixedLengthIndexable(length.object.definition.type, |
| 326 allowNull: true); | 359 allowNull: true); |
| 327 } | 360 } |
| 328 | 361 |
| 329 /// 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 |
| 330 /// be hoisted out of [loop] without changing its value. | 363 /// be hoisted out of [loop] without changing its value or changing the timing |
| 364 /// of a thrown exception. |
| 331 bool canHoistHeapDependencyOutOfLoop(Primitive prim, Continuation loop) { | 365 bool canHoistHeapDependencyOutOfLoop(Primitive prim, Continuation loop) { |
| 332 assert(prim.isSafeForElimination); | 366 // 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 |
| 368 // [loop] is the current loop header, but for other loops we just give up. |
| 369 if (!prim.isSafeForElimination && loop != currentLoopHeader) { |
| 370 return false; |
| 371 } |
| 333 if (prim is GetLength && !isImmutableLength(prim)) { | 372 if (prim is GetLength && !isImmutableLength(prim)) { |
| 334 return !loopEffects.loopChangesLength(loop); | 373 return !loopEffects.loopChangesLength(loop); |
| 335 } else if (prim is GetField && !isImmutable(prim.field)) { | 374 } else if (prim is GetField && !isImmutable(prim.field)) { |
| 336 return !loopEffects.getSideEffectsInLoop(loop).changesInstanceProperty(); | 375 return !loopEffects.getSideEffectsInLoop(loop).changesInstanceProperty(); |
| 337 } else if (prim is GetStatic && !isImmutable(prim.element)) { | 376 } else if (prim is GetStatic && !isImmutable(prim.element)) { |
| 338 return !loopEffects.getSideEffectsInLoop(loop).changesStaticProperty(); | 377 return !loopEffects.getSideEffectsInLoop(loop).changesStaticProperty(); |
| 339 } else if (prim is GetIndex) { | 378 } else if (prim is GetIndex) { |
| 340 return !loopEffects.getSideEffectsInLoop(loop).changesIndex(); | 379 return !loopEffects.getSideEffectsInLoop(loop).changesIndex(); |
| 341 } else { | 380 } else { |
| 342 return true; | 381 return true; |
| 343 } | 382 } |
| 344 } | 383 } |
| 345 | 384 |
| 346 | |
| 347 // ------------------ TRAVERSAL AND EFFECT NUMBERING --------------------- | 385 // ------------------ TRAVERSAL AND EFFECT NUMBERING --------------------- |
| 348 // | 386 // |
| 349 // These methods traverse the IR while updating the current effect numbers. | 387 // These methods traverse the IR while updating the current effect numbers. |
| 350 // They are not specific to GVN. | 388 // They are not specific to GVN. |
| 351 // | 389 // |
| 352 // TODO(asgerf): Avoid duplicated code for side effect analysis. | 390 // TODO(asgerf): Avoid duplicated code for side effect analysis. |
| 353 // Should be easier to fix once primitives and call expressions are the same. | 391 // Should be easier to fix once primitives and call expressions are the same. |
| 354 | 392 |
| 355 void addSideEffects(SideEffects fx, {bool length: true}) { | 393 void addSideEffects(SideEffects fx, {bool length: true}) { |
| 356 if (fx.changesInstanceProperty()) { | 394 if (fx.changesInstanceProperty()) { |
| (...skipping 365 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 722 | 760 |
| 723 @override | 761 @override |
| 724 processReference(Reference ref) { | 762 processReference(Reference ref) { |
| 725 callback(ref); | 763 callback(ref); |
| 726 } | 764 } |
| 727 | 765 |
| 728 static void forEach(Primitive node, ReferenceCallback callback) { | 766 static void forEach(Primitive node, ReferenceCallback callback) { |
| 729 new InputVisitor(callback).visit(node); | 767 new InputVisitor(callback).visit(node); |
| 730 } | 768 } |
| 731 } | 769 } |
| OLD | NEW |