| 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;
|
| + }
|
| +}
|
|
|