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

Unified Diff: pkg/compiler/lib/src/ssa/builder_kernel.dart

Issue 2377813002: kernel->ssa: implement for-in loops (Closed)
Patch Set: fix another import Created 4 years, 3 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 | « pkg/compiler/lib/src/ssa/builder.dart ('k') | pkg/compiler/lib/src/ssa/kernel_ast_adapter.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/compiler/lib/src/ssa/builder_kernel.dart
diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart
index 2d4726d25d3cc488632310f9cf854ec57ee0ae7e..807c1fcfaf80b62144225e8d3a6c3b962f29c827 100644
--- a/pkg/compiler/lib/src/ssa/builder_kernel.dart
+++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart
@@ -6,6 +6,7 @@ import 'package:kernel/ast.dart' as ir;
import '../common.dart';
import '../common/codegen.dart' show CodegenRegistry, CodegenWorkItem;
+import '../common/names.dart';
import '../common/tasks.dart' show CompilerTask;
import '../compiler.dart';
import '../dart_types.dart';
@@ -17,7 +18,6 @@ import '../resolution/tree_elements.dart';
import '../tree/dartstring.dart';
import '../types/masks.dart';
import '../universe/selector.dart';
-
import 'graph_builder.dart';
import 'kernel_ast_adapter.dart';
import 'kernel_string_builder.dart';
@@ -242,6 +242,164 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder {
}
@override
+ void visitForInStatement(ir.ForInStatement forInStatement) {
+ if (forInStatement.isAsync) {
+ compiler.reporter.internalError(astAdapter.getNode(forInStatement),
+ "Cannot compile async for-in using kernel.");
+ }
+ // If the expression being iterated over is a JS indexable type, we can
+ // generate an optimized version of for-in that uses indexing.
+ if (astAdapter.isJsIndexableIterator(forInStatement)) {
+ _buildForInIndexable(forInStatement);
+ } else {
+ _buildForInIterator(forInStatement);
+ }
+ }
+
+ /// Builds the graph for a for-in node with an indexable expression.
+ ///
+ /// In this case we build:
+ ///
+ /// int end = a.length;
+ /// for (int i = 0;
+ /// i < a.length;
+ /// checkConcurrentModificationError(a.length == end, a), ++i) {
+ /// <declaredIdentifier> = a[i];
+ /// <body>
+ /// }
+ _buildForInIndexable(ir.ForInStatement forInStatement) {
+ SyntheticLocal indexVariable = new SyntheticLocal('_i', targetElement);
+
+ // These variables are shared by initializer, condition, body and update.
+ HInstruction array; // Set in buildInitializer.
+ bool isFixed; // Set in buildInitializer.
+ HInstruction originalLength = null; // Set for growable lists.
+
+ HInstruction buildGetLength() {
+ HFieldGet result = new HFieldGet(
+ astAdapter.jsIndexableLength, array, backend.positiveIntType,
+ isAssignable: !isFixed);
+ add(result);
+ return result;
+ }
+
+ void buildConcurrentModificationErrorCheck() {
+ if (originalLength == null) return;
+ // The static call checkConcurrentModificationError() is expanded in
+ // codegen to:
+ //
+ // array.length == _end || throwConcurrentModificationError(array)
+ //
+ HInstruction length = buildGetLength();
+ push(new HIdentity(length, originalLength, null, backend.boolType));
+ _pushStaticInvocation(
+ astAdapter.checkConcurrentModificationError,
+ [pop(), array],
+ astAdapter.checkConcurrentModificationErrorReturnType);
+ pop();
+ }
+
+ void buildInitializer() {
+ forInStatement.iterable.accept(this);
+ array = pop();
+ isFixed = astAdapter.isFixedLength(array.instructionType);
+ localsHandler.updateLocal(
+ indexVariable, graph.addConstantInt(0, compiler));
+ originalLength = buildGetLength();
+ }
+
+ HInstruction buildCondition() {
+ HInstruction index = localsHandler.readLocal(indexVariable);
+ HInstruction length = buildGetLength();
+ HInstruction compare = new HLess(index, length, null, backend.boolType);
+ add(compare);
+ return compare;
+ }
+
+ void buildBody() {
+ // If we had mechanically inlined ArrayIterator.moveNext(), it would have
+ // inserted the ConcurrentModificationError check as part of the
+ // condition. It is not necessary on the first iteration since there is
+ // no code between calls to `get iterator` and `moveNext`, so the test is
+ // moved to the loop update.
+
+ // Find a type for the element. Use the element type of the indexer of the
+ // array, as this is stronger than the iterator's `get current` type, for
+ // example, `get current` includes null.
+ // TODO(sra): The element type of a container type mask might be better.
+ TypeMask type = astAdapter.inferredIndexType(forInStatement);
+
+ HInstruction index = localsHandler.readLocal(indexVariable);
+ HInstruction value = new HIndex(array, index, null, type);
+ add(value);
+
+ localsHandler.updateLocal(
+ astAdapter.getLocal(forInStatement.variable), value);
+
+ forInStatement.body.accept(this);
+ }
+
+ void buildUpdate() {
+ // See buildBody as to why we check here.
+ buildConcurrentModificationErrorCheck();
+
+ // TODO(sra): It would be slightly shorter to generate `a[i++]` in the
+ // body (and that more closely follows what an inlined iterator would do)
+ // but the code is horrible as `i+1` is carried around the loop in an
+ // additional variable.
+ HInstruction index = localsHandler.readLocal(indexVariable);
+ HInstruction one = graph.addConstantInt(1, compiler);
+ HInstruction addInstruction =
+ new HAdd(index, one, null, backend.positiveIntType);
+ add(addInstruction);
+ localsHandler.updateLocal(indexVariable, addInstruction);
+ }
+
+ loopHandler.handleLoop(forInStatement, buildInitializer, buildCondition,
+ buildUpdate, buildBody);
+ }
+
+ _buildForInIterator(ir.ForInStatement forInStatement) {
+ // Generate a structure equivalent to:
+ // Iterator<E> $iter = <iterable>.iterator;
+ // while ($iter.moveNext()) {
+ // <declaredIdentifier> = $iter.current;
+ // <body>
+ // }
+
+ // The iterator is shared between initializer, condition and body.
+ HInstruction iterator;
+
+ void buildInitializer() {
+ TypeMask mask = astAdapter.typeOfIterator(forInStatement);
+ forInStatement.iterable.accept(this);
+ HInstruction receiver = pop();
+ _pushDynamicInvocation(forInStatement, mask, <HInstruction>[receiver],
+ selector: Selectors.iterator);
+ iterator = pop();
+ }
+
+ HInstruction buildCondition() {
+ TypeMask mask = astAdapter.typeOfIteratorMoveNext(forInStatement);
+ _pushDynamicInvocation(forInStatement, mask, <HInstruction>[iterator],
+ selector: Selectors.moveNext);
+ return popBoolified();
+ }
+
+ void buildBody() {
+ TypeMask mask = astAdapter.typeOfIteratorCurrent(forInStatement);
+ _pushDynamicInvocation(forInStatement, mask, [iterator],
+ selector: Selectors.current);
+ localsHandler.updateLocal(
+ astAdapter.getLocal(forInStatement.variable), pop());
+ forInStatement.body.accept(this);
+ }
+
+ loopHandler.handleLoop(
+ forInStatement, buildInitializer, buildCondition, () {}, buildBody);
+ }
+
+ @override
void visitWhileStatement(ir.WhileStatement whileStatement) {
assert(isReachable);
HInstruction buildCondition() {
@@ -330,7 +488,10 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder {
// TODO(het): set runtime type info
}
- // TODO(het): Set the instruction type to the list type given by inference
+ TypeMask type = astAdapter.typeOfNewList(targetElement, listLiteral);
+ if (!type.containsAll(compiler.closedWorld)) {
+ listInstruction.instructionType = type;
+ }
stack.add(listInstruction);
}
@@ -412,23 +573,13 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder {
propertyGet.receiver.accept(this);
HInstruction receiver = pop();
- List<HInstruction> inputs = <HInstruction>[];
- bool isIntercepted = astAdapter.isIntercepted(propertyGet);
- if (isIntercepted) {
- HInterceptor interceptor = _interceptorFor(receiver);
- inputs.add(interceptor);
- }
- inputs.add(receiver);
-
- TypeMask type = astAdapter.selectorGetterTypeOf(propertyGet);
-
- push(new HInvokeDynamicGetter(astAdapter.getGetterSelector(propertyGet),
- astAdapter.typeOfGet(propertyGet), null, inputs, type));
+ _pushDynamicInvocation(propertyGet, astAdapter.typeOfGet(propertyGet),
+ <HInstruction>[receiver]);
}
@override
void visitVariableGet(ir.VariableGet variableGet) {
- LocalElement local = astAdapter.getElement(variableGet.variable);
+ Local local = astAdapter.getLocal(variableGet.variable);
stack.add(localsHandler.readLocal(local));
}
@@ -441,7 +592,7 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder {
@override
void visitVariableDeclaration(ir.VariableDeclaration declaration) {
- LocalElement local = astAdapter.getElement(declaration);
+ Local local = astAdapter.getLocal(declaration);
if (declaration.initializer == null) {
HInstruction initialValue = graph.addConstantNull(compiler);
localsHandler.updateLocal(local, initialValue);
@@ -508,28 +659,43 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder {
push(instruction);
}
- // TODO(het): Decide when to inline
- @override
- void visitMethodInvocation(ir.MethodInvocation invocation) {
- invocation.receiver.accept(this);
- HInstruction receiver = pop();
-
- List<HInstruction> arguments = <HInstruction>[receiver]
- ..addAll(_visitArguments(invocation.arguments));
-
+ void _pushDynamicInvocation(
+ ir.Node node, TypeMask mask, List<HInstruction> arguments,
+ {Selector selector}) {
+ HInstruction receiver = arguments.first;
List<HInstruction> inputs = <HInstruction>[];
- bool isIntercepted = astAdapter.isIntercepted(invocation);
+ selector ??= astAdapter.getSelector(node);
+ bool isIntercepted = astAdapter.isInterceptedSelector(selector);
+
if (isIntercepted) {
HInterceptor interceptor = _interceptorFor(receiver);
inputs.add(interceptor);
}
inputs.addAll(arguments);
- TypeMask type = astAdapter.selectorTypeOf(invocation);
+ TypeMask type = astAdapter.selectorTypeOf(selector, mask);
+ if (selector.isGetter) {
+ push(new HInvokeDynamicGetter(selector, mask, null, inputs, type));
+ } else if (selector.isSetter) {
+ push(new HInvokeDynamicSetter(selector, mask, null, inputs, type));
+ } else {
+ push(new HInvokeDynamicMethod(
+ selector, mask, inputs, type, isIntercepted));
+ }
+ }
+
+ // TODO(het): Decide when to inline
+ @override
+ void visitMethodInvocation(ir.MethodInvocation invocation) {
+ invocation.receiver.accept(this);
+ HInstruction receiver = pop();
- push(new HInvokeDynamicMethod(astAdapter.getSelector(invocation),
- astAdapter.typeOfInvocation(invocation), inputs, type, isIntercepted));
+ _pushDynamicInvocation(
+ invocation,
+ astAdapter.typeOfInvocation(invocation),
+ <HInstruction>[receiver]
+ ..addAll(_visitArguments(invocation.arguments)));
}
HInterceptor _interceptorFor(HInstruction intercepted) {
« no previous file with comments | « pkg/compiler/lib/src/ssa/builder.dart ('k') | pkg/compiler/lib/src/ssa/kernel_ast_adapter.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698