Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(78)

Unified Diff: sdk/lib/_internal/compiler/implementation/types/inferrer_visitor.dart

Issue 23050002: Handle switch with default case, and do/while loop better in the inferrer. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | tests/compiler/dart2js/simple_inferrer_test.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: sdk/lib/_internal/compiler/implementation/types/inferrer_visitor.dart
===================================================================
--- sdk/lib/_internal/compiler/implementation/types/inferrer_visitor.dart (revision 26047)
+++ sdk/lib/_internal/compiler/implementation/types/inferrer_visitor.dart (working copy)
@@ -224,14 +224,23 @@
variables.forEach(f);
}
- void forEachLocalUntil(Node node, void f(Element, T type)) {
- forEachOwnLocal(f);
+ void forEachLocalUntilNode(Node node,
+ void f(Element element, T type),
+ [Set<Element> seenLocals]) {
+ if (seenLocals == null) seenLocals = new Set<Element>();
+ if (variables != null) {
+ variables.forEach((element, type) {
+ if (seenLocals.contains(element)) return;
+ seenLocals.add(element);
+ f(element, type);
+ });
+ }
if (block == node) return;
- if (parent != null) parent.forEachLocalUntil(node, f);
+ if (parent != null) parent.forEachLocalUntilNode(node, f, seenLocals);
}
void forEachLocal(void f(Element, T type)) {
- forEachLocalUntil(null, f);
+ forEachLocalUntilNode(null, f);
}
void remove(Element element) {
@@ -410,13 +419,31 @@
/**
* Merge all [LocalsHandler] in [handlers] into [:this:]. Returns
* whether a local in [:this:] has changed.
+ *
+ * If [keepOwnLocals] is true, the types of locals in this
+ * [LocalsHandler] are being used in the merge. [keepOwnLocals]
+ * should be true if this [LocalsHandler], the dominator of
+ * all [handlers], also direclty flows into the join point,
+ * that is the code after all [handlers]. For example, consider:
+ *
+ * [: switch (...) {
+ * case 1: ...; break;
+ * }
+ * :]
+ *
+ * The [LocalsHandler] at entry of the switch also flows into the
+ * exit of the switch, because there is no default case. So the
+ * types of locals at entry of the switch have to take part to the
+ * merge.
*/
- bool mergeAll(List<LocalsHandler<T>> handlers) {
+ bool mergeAll(List<LocalsHandler<T>> handlers,
+ {bool keepOwnLocals: true}) {
bool changed = false;
- handlers.forEach((LocalsHandler<T> handler) {
- if (handler.seenReturnOrThrow) return;
- Node level = locals.block;
- handler.locals.forEachLocalUntil(level, (Element local, T otherType) {
+ Node level = locals.block;
+
+ void mergeHandler(LocalsHandler<T> other) {
+ if (other.seenReturnOrThrow) return;
+ other.locals.forEachLocalUntilNode(level, (local, otherType) {
T myType = locals[local];
if (myType == null) return;
T newType = types.addPhiInput(local, myType, otherType);
@@ -425,7 +452,40 @@
locals[local] = newType;
}
});
- });
+ }
+
+ if (keepOwnLocals && !seenReturnOrThrow) {
+ handlers.forEach(mergeHandler);
+ } else {
+ // Find the first handler that does not abort.
+ int index = 0;
+ LocalsHandler<T> startWith;
+ while (index < handlers.length
+ && (startWith = handlers[index]).seenReturnOrThrow) {
+ index++;
+ }
+ if (index == handlers.length) {
+ // If we haven't found a handler that does not abort, we know
+ // this handler aborts.
+ seenReturnOrThrow = true;
+ } else {
+ // Otherwise, this handler does not abort.
+ seenReturnOrThrow = false;
+ // Use [startWith] to initialize the types of locals.
+ startWith.locals.forEachLocalUntilNode(level, (local, otherType) {
+ T myType = locals[local];
+ if (myType == null) return;
+ if (myType != otherType) {
+ changed = true;
+ locals[local] = otherType;
+ }
+ });
+ // Merge all other handlers.
+ for (int i = index + 1; i < handlers.length; i++) {
+ mergeHandler(handlers[i]);
+ }
+ }
+ }
return changed;
}
@@ -795,14 +855,22 @@
loopLevel++;
bool changed = false;
TargetElement target = elements[node];
- setupBreaksAndContinues(target);
locals.startLoop(node);
LocalsHandler<T> saved;
+ bool keepOwnLocals = node.asDoWhile() == null;
do {
+ // Setup (and clear in case of multiple iterations of the loop)
+ // the lists of breaks and continues seen in the loop.
+ setupBreaksAndContinues(target);
saved = locals;
locals = new LocalsHandler<T>.from(locals, node);
logic();
- changed = saved.mergeAll(getLoopBackEdges(target));
+ changed = saved.mergeAll(getLoopBackEdges(target),
+ keepOwnLocals: keepOwnLocals);
+ // Now that we have done the initial merge, following merges to
+ // reach a fixed point must use the previously computed types of
+ // locals at entry of the loop.
+ keepOwnLocals = true;
locals = saved;
} while (changed);
loopLevel--;
@@ -973,25 +1041,20 @@
} else {
LocalsHandler<T> saved = locals;
List<LocalsHandler<T>> localsToMerge = <LocalsHandler<T>>[];
+ bool hasDefaultCase = false;
for (SwitchCase switchCase in node.cases) {
+ if (switchCase.isDefaultCase) {
+ hasDefaultCase = true;
+ }
locals = new LocalsHandler<T>.from(saved, switchCase);
visit(switchCase);
localsToMerge.add(locals);
}
- saved.mergeAll(localsToMerge);
+ saved.mergeAll(localsToMerge, keepOwnLocals: !hasDefaultCase);
locals = saved;
}
clearBreaksAndContinues(elements[node]);
- // In case there is a default in the switch we discard the
- // incoming localsHandler, because the types it holds do not need
- // to be merged after the switch statement. This means that, if all
- // cases, including the default, break or continue, the [result]
- // handler may think it just aborts the current block. Therefore
- // we set the current locals to not have any break or continue, so
- // that the [visitBlock] method does not assume the code after the
- // switch is dead code.
- locals.seenBreakOrContinue = false;
}
T visitCascadeReceiver(CascadeReceiver node) {
« no previous file with comments | « no previous file | tests/compiler/dart2js/simple_inferrer_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698