Index: pkg/compiler/lib/src/cps_ir/share_interceptors.dart |
diff --git a/pkg/compiler/lib/src/cps_ir/share_interceptors.dart b/pkg/compiler/lib/src/cps_ir/share_interceptors.dart |
index 3706725e3d102a8c63b652ec7fe196761d9e435c..bdfc0830e1d4746abfcaef62978292c262e693ab 100644 |
--- a/pkg/compiler/lib/src/cps_ir/share_interceptors.dart |
+++ b/pkg/compiler/lib/src/cps_ir/share_interceptors.dart |
@@ -7,10 +7,15 @@ library dart2js.cps_ir.share_interceptors; |
import 'optimizers.dart'; |
import 'cps_ir_nodes.dart'; |
import 'loop_hierarchy.dart'; |
+import 'cps_fragment.dart'; |
import '../constants/values.dart'; |
+import '../elements/elements.dart'; |
+import '../js_backend/js_backend.dart' show JavaScriptBackend; |
+import '../types/types.dart' show TypeMask; |
+import '../io/source_information.dart' show SourceInformation; |
/// Removes redundant `getInterceptor` calls. |
-/// |
+/// |
/// The pass performs three optimizations for interceptors: |
///- pull interceptors out of loops |
///- replace interceptors with constants |
@@ -23,31 +28,27 @@ class ShareInterceptors extends TrampolineRecursiveVisitor implements Pass { |
<Primitive, Continuation>{}; |
/// An interceptor currently in scope for a given primitive. |
- final Map<Primitive, Primitive> interceptorFor = |
- <Primitive, Primitive>{}; |
+ final Map<Primitive, Interceptor> interceptorFor = <Primitive, Interceptor>{}; |
- /// A primitive currently in scope holding a given interceptor constant. |
- final Map<ConstantValue, Primitive> sharedConstantFor = |
- <ConstantValue, Primitive>{}; |
- |
- /// Interceptors to be hoisted out of the given loop. |
- final Map<Continuation, List<Primitive>> loopHoistedInterceptors = |
- <Continuation, List<Primitive>>{}; |
+ /// Interceptors that have been hoisted out of a given loop. |
+ final Map<Continuation, List<Interceptor>> loopHoistedInterceptors = |
+ <Continuation, List<Interceptor>>{}; |
+ JavaScriptBackend backend; |
LoopHierarchy loopHierarchy; |
Continuation currentLoopHeader; |
+ ShareInterceptors(this.backend); |
+ |
void rewrite(FunctionDefinition node) { |
loopHierarchy = new LoopHierarchy(node); |
visit(node.body); |
+ new ShareConstants().visit(node); |
} |
@override |
Expression traverseContinuation(Continuation cont) { |
Continuation oldLoopHeader = currentLoopHeader; |
- pushAction(() { |
- currentLoopHeader = oldLoopHeader; |
- }); |
currentLoopHeader = loopHierarchy.getLoopHeader(cont); |
for (Parameter param in cont.parameters) { |
loopHeaderFor[param] = currentLoopHeader; |
@@ -57,26 +58,118 @@ class ShareInterceptors extends TrampolineRecursiveVisitor implements Pass { |
// After the loop body has been processed, all interceptors hoisted |
// to this loop fall out of scope and should be removed from the |
// environment. |
- List<Primitive> hoisted = loopHoistedInterceptors[cont]; |
+ List<Interceptor> hoisted = loopHoistedInterceptors[cont]; |
if (hoisted != null) { |
- for (Primitive interceptor in hoisted) { |
- if (interceptor is Interceptor) { |
- Primitive input = interceptor.input.definition; |
- assert(interceptorFor[input] == interceptor); |
- interceptorFor.remove(input); |
- } else if (interceptor is Constant) { |
- assert(sharedConstantFor[interceptor.value] == interceptor); |
- sharedConstantFor.remove(interceptor.value); |
- } else { |
- throw "Unexpected interceptor: $interceptor"; |
- } |
+ for (Interceptor interceptor in hoisted) { |
+ Primitive input = interceptor.input.definition; |
+ assert(interceptorFor[input] == interceptor); |
+ interceptorFor.remove(input); |
+ constifyInterceptor(interceptor); |
} |
} |
}); |
} |
+ pushAction(() { |
+ currentLoopHeader = oldLoopHeader; |
+ }); |
return cont.body; |
} |
+ /// If only one method table can be returned by the given interceptor, |
+ /// returns a constant for that method table. |
+ InterceptorConstantValue getInterceptorConstant(Interceptor node) { |
+ if (node.interceptedClasses.length == 1 && |
+ node.isInterceptedClassAlwaysExact) { |
+ ClassElement interceptorClass = node.interceptedClasses.single; |
+ return new InterceptorConstantValue(interceptorClass.rawType); |
+ } |
+ return null; |
+ } |
+ |
+ bool hasNoFalsyValues(ClassElement class_) { |
+ return class_ != backend.jsInterceptorClass && |
+ class_ != backend.jsNullClass && |
+ class_ != backend.jsBoolClass && |
+ class_ != backend.jsStringClass && |
+ !class_.isSubclassOf(backend.jsNumberClass); |
+ } |
+ |
+ Continuation getCurrentOuterLoop({Continuation scope}) { |
+ Continuation inner = null, outer = currentLoopHeader; |
+ while (outer != scope) { |
+ inner = outer; |
+ outer = loopHierarchy.getEnclosingLoop(outer); |
+ } |
+ return inner; |
+ } |
+ |
+ /// Binds the given constant in a primitive, in scope of the [useSite]. |
+ /// |
+ /// The constant will be hoisted out of loops, and shared with other requests |
+ /// for the same constant as long as it is in scope. |
+ Primitive makeConstantFor(ConstantValue constant, |
+ {Expression useSite, |
+ TypeMask type, |
+ SourceInformation sourceInformation, |
+ Entity hint}) { |
+ Constant prim = |
+ new Constant(constant, sourceInformation: sourceInformation); |
+ prim.hint = hint; |
+ prim.type = type; |
+ LetPrim letPrim = new LetPrim(prim); |
+ Continuation loop = getCurrentOuterLoop(); |
+ if (loop != null) { |
+ LetCont loopBinding = loop.parent; |
+ letPrim.insertAbove(loopBinding); |
+ } else { |
+ letPrim.insertAbove(useSite); |
+ } |
+ return prim; |
+ } |
+ |
+ void constifyInterceptor(Interceptor interceptor) { |
+ LetPrim let = interceptor.parent; |
+ InterceptorConstantValue constant = getInterceptorConstant(interceptor); |
+ |
+ if (constant == null) return; |
+ |
+ if (interceptor.isAlwaysIntercepted) { |
+ Primitive constantPrim = makeConstantFor(constant, |
+ useSite: let, |
+ type: interceptor.type, |
+ sourceInformation: interceptor.sourceInformation); |
+ constantPrim.useElementAsHint(interceptor.hint); |
+ constantPrim.substituteFor(interceptor); |
+ interceptor.destroy(); |
+ let.remove(); |
+ } else if (interceptor.isAlwaysNullOrIntercepted) { |
+ Primitive input = interceptor.input.definition; |
+ Primitive constantPrim = makeConstantFor(constant, |
+ useSite: let, |
+ type: interceptor.type.nonNullable(), |
+ sourceInformation: interceptor.sourceInformation); |
+ CpsFragment cps = new CpsFragment(interceptor.sourceInformation); |
+ Parameter param = new Parameter(interceptor.hint); |
+ Continuation cont = cps.letCont(<Parameter>[param]); |
+ if (interceptor.interceptedClasses.every(hasNoFalsyValues)) { |
+ // If null is the only falsy value, compile as "x && CONST". |
+ cps.ifFalsy(input).invokeContinuation(cont, [input]); |
+ } else { |
+ // If there are other falsy values compile as "x == null ? x : CONST". |
+ Primitive condition = cps.applyBuiltin( |
+ BuiltinOperator.LooseEq, |
+ [input, cps.makeNull()]); |
+ cps.ifTruthy(condition).invokeContinuation(cont, [input]); |
+ } |
+ cps.invokeContinuation(cont, [constantPrim]); |
+ cps.context = cont; |
+ cps.insertAbove(let); |
+ param.substituteFor(interceptor); |
+ interceptor.destroy(); |
+ let.remove(); |
+ } |
+ } |
+ |
@override |
Expression traverseLetPrim(LetPrim node) { |
loopHeaderFor[node.primitive] = currentLoopHeader; |
@@ -88,88 +181,45 @@ class ShareInterceptors extends TrampolineRecursiveVisitor implements Pass { |
Primitive input = interceptor.input.definition; |
// Try to reuse an existing interceptor for the same input. |
- Primitive existing = interceptorFor[input]; |
+ Interceptor existing = interceptorFor[input]; |
if (existing != null) { |
- if (existing is Interceptor) { |
- existing.interceptedClasses.addAll(interceptor.interceptedClasses); |
- } |
+ existing.interceptedClasses.addAll(interceptor.interceptedClasses); |
+ existing.flags |= interceptor.flags; |
existing.substituteFor(interceptor); |
interceptor.destroy(); |
node.remove(); |
return next; |
} |
- // There is no interceptor obtained from this particular input, but |
- // there might one obtained from another input that is known to |
- // have the same result, so try to reuse that. |
- InterceptorConstantValue constant = interceptor.constantValue; |
- if (constant != null) { |
- existing = sharedConstantFor[constant]; |
- if (existing != null) { |
- existing.substituteFor(interceptor); |
- interceptor.destroy(); |
- node.remove(); |
- return next; |
- } |
- |
- // The interceptor could not be shared. Replace it with a constant. |
- Constant constantPrim = new Constant(constant); |
- node.primitive = constantPrim; |
- constantPrim.parent = node; |
- constantPrim.hint = interceptor.hint; |
- constantPrim.type = interceptor.type; |
- constantPrim.substituteFor(interceptor); |
- interceptor.destroy(); |
- sharedConstantFor[constant] = constantPrim; |
- } else { |
- interceptorFor[input] = interceptor; |
- } |
+ // Put this interceptor in the environment. |
+ interceptorFor[input] = interceptor; |
- // Determine the outermost loop where the input to the interceptor call |
- // is available. Constant interceptors take no input and can thus be |
- // hoisted all way to the top-level. |
- Continuation referencedLoop = constant != null |
- ? null |
- : lowestCommonAncestor(loopHeaderFor[input], currentLoopHeader); |
+ // Determine how far the interceptor can be lifted. The outermost loop |
+ // that contains the input binding should also contain the interceptor |
+ // binding. |
+ Continuation referencedLoop = |
+ lowestCommonAncestor(loopHeaderFor[input], currentLoopHeader); |
if (referencedLoop != currentLoopHeader) { |
- // [referencedLoop] contains the binding for [input], so we cannot hoist |
- // the interceptor outside that loop. Find the loop nested one level |
- // inside referencedLoop, and hoist the interceptor just outside that one. |
- Continuation loop = currentLoopHeader; |
- Continuation enclosing = loopHierarchy.getEnclosingLoop(loop); |
- while (enclosing != referencedLoop) { |
- assert(loop != null); |
- loop = enclosing; |
- enclosing = loopHierarchy.getEnclosingLoop(loop); |
- } |
- assert(loop != null); |
- |
- // Move the LetPrim above the loop binding. |
- LetCont loopBinding = loop.parent; |
+ Continuation hoistTarget = getCurrentOuterLoop(scope: referencedLoop); |
+ LetCont loopBinding = hoistTarget.parent; |
node.remove(); |
node.insertAbove(loopBinding); |
- |
- // A different loop now contains the interceptor. |
- loopHeaderFor[node.primitive] = enclosing; |
- |
- // Register the interceptor as hoisted to that loop, so it will be |
- // removed from the environment when it falls out of scope. |
+ // Remove the interceptor from the environment after processing the loop. |
loopHoistedInterceptors |
- .putIfAbsent(loop, () => <Primitive>[]) |
- .add(node.primitive); |
- } else if (constant != null) { |
- // The LetPrim was not hoisted. Remove the bound interceptor from the |
- // environment when leaving the LetPrim body. |
- pushAction(() { |
- assert(sharedConstantFor[constant] == node.primitive); |
- sharedConstantFor.remove(constant); |
- }); |
+ .putIfAbsent(hoistTarget, () => <Interceptor>[]) |
+ .add(interceptor); |
} else { |
+ // Remove the interceptor from the environment when it falls out of scope. |
pushAction(() { |
- assert(interceptorFor[input] == node.primitive); |
+ assert(interceptorFor[input] == interceptor); |
interceptorFor.remove(input); |
+ |
+ // Now that the final set of intercepted classes has been seen, try to |
+ // replace it with a constant. |
+ constifyInterceptor(interceptor); |
}); |
} |
+ |
return next; |
} |
@@ -194,3 +244,32 @@ class ShareInterceptors extends TrampolineRecursiveVisitor implements Pass { |
return loopHierarchy.loopDepth[loop]; |
} |
} |
+ |
+class ShareConstants extends TrampolineRecursiveVisitor { |
+ Map<ConstantValue, Constant> sharedConstantFor = <ConstantValue, Constant>{}; |
+ |
+ Expression traverseLetPrim(LetPrim node) { |
+ Expression next = node.body; |
+ if (node.primitive is Constant && shouldShareConstant(node.primitive)) { |
+ Constant prim = node.primitive; |
+ Constant existing = sharedConstantFor[prim.value]; |
+ if (existing != null) { |
+ existing.substituteFor(prim); |
+ existing.useElementAsHint(prim.hint); |
+ prim.destroy(); |
+ node.remove(); |
+ return next; |
+ } |
+ sharedConstantFor[prim.value] = prim; |
+ pushAction(() { |
+ assert(sharedConstantFor[prim.value] == prim); |
+ sharedConstantFor.remove(prim.value); |
+ }); |
+ } |
+ return next; |
+ } |
+ |
+ bool shouldShareConstant(Constant constant) { |
+ return constant.value.isInterceptor; |
+ } |
+} |