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

Unified Diff: lib/compiler/implementation/ssa/builder.dart

Issue 10873025: Inlining of static functions: applying Florian's CL. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 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
Index: lib/compiler/implementation/ssa/builder.dart
===================================================================
--- lib/compiler/implementation/ssa/builder.dart (revision 11216)
+++ lib/compiler/implementation/ssa/builder.dart (working copy)
@@ -837,10 +837,16 @@
activationVariables = new Map<Element, HLocalValue>(),
jumpTargets = new Map<TargetElement, JumpHandler>(),
parameters = new Map<Element, HParameterValue>(),
+ inliningStack = <InliningState>[],
super(work.resolutionTree) {
localsHandler = new LocalsHandler(this);
}
+ static final MAX_INLINING_DEPTH = 3;
+ static final MAX_INLINING_SOURCE_SIZE = 100;
+ List<InliningState> inliningStack;
+ Element returnElement = null;
+
void disableMethodInterception() {
assert(methodInterceptionEnabled);
methodInterceptionEnabled = false;
@@ -900,6 +906,84 @@
return bodyElement;
}
+ InliningState enterInlinedMethod(PartialFunctionElement function,
+ Selector selector,
+ Link<Node> arguments) {
+ // Once we start to compile the arguments we must be sure that we don't
+ // abort.
+ List<HInstruction> compiledArguments = new List<HInstruction>();
+ bool succeeded = addStaticSendArgumentsToList(selector,
+ arguments,
+ function,
+ compiledArguments);
+ assert(succeeded);
+
+ InliningState state =
+ new InliningState(function, returnElement, elements, stack);
+ inliningStack.add(state);
+ stack = <HInstruction>[];
+ returnElement = new Element(const SourceString("result"),
+ ElementKind.VARIABLE,
+ function);
+ localsHandler.updateLocal(returnElement, graph.addConstantNull());
+ elements = compiler.enqueuer.resolution.getCachedElements(function);
+ FunctionSignature signature = function.computeSignature(compiler);
+ int index = 0;
+ signature.forEachParameter((Element parameter) {
+ HInstruction argument = compiledArguments[index++];
+ localsHandler.updateLocal(parameter, argument);
+ potentiallyCheckType(argument, parameter);
+ });
+ return state;
+ }
+
+ void leaveInlinedMethod(InliningState state) {
+ InliningState poppedState = inliningStack.removeLast();
+ assert(state == poppedState);
+ elements = state.oldElements;
+ stack.add(localsHandler.readLocal(returnElement));
+ returnElement = state.oldReturnElement;
+ assert(stack.length == 1);
+ state.oldStack.add(stack[0]);
+ stack = state.oldStack;
+ }
+
+ bool tryInlineMethod(Element element,
+ Selector selector,
+ Link<Node> arguments) {
+ if (element.kind != ElementKind.FUNCTION) return false;
+ if (element is !PartialFunctionElement) return false;
+ if (inliningStack.length > MAX_INLINING_DEPTH) return false;
+ // Don't inline recursive calls. We use the same elements for the inlined
+ // functions and would thus clobber our local variables.
+ if (work.element == element) return false;
+ for (int i = 0; i < inliningStack.length; i++) {
+ if (inliningStack[i].function == element) return false;
+ }
+ // TODO(ngeoffray): Inlining currently does not work in the presence of
kasperl 2012/08/23 09:18:51 I wonder if this restriction could be lifted. Mayb
+ // private calls.
+ if (currentLibrary != element.getLibrary()) return false;
+ PartialFunctionElement function = element;
+ int sourceSize =
+ function.endToken.charOffset - function.beginToken.charOffset;
+ if (sourceSize > MAX_INLINING_SOURCE_SIZE) return false;
+ if (!selector.applies(function, compiler)) return false;
+ FunctionExpression functionExpression = function.parseNode(compiler);
+ TreeElements newElements =
+ compiler.enqueuer.resolution.getCachedElements(function);
+ if (newElements === null) {
+ compiler.internalError("Element not resolved: $function");
+ }
+ if (!InlineWeeder.canBeInlined(functionExpression, newElements)) {
+ return false;
+ }
+
+ InliningState state = enterInlinedMethod(function, selector, arguments);
+ functionExpression.body.accept(this);
+ leaveInlinedMethod(state);
+ return true;
+ }
+
void inlineSuperOrRedirect(FunctionElement constructor,
Selector selector,
Link<Node> arguments,
@@ -2363,6 +2447,9 @@
return;
}
compiler.ensure(element.kind !== ElementKind.GENERATIVE_CONSTRUCTOR);
+
+ if (tryInlineMethod(element, selector, node.arguments)) return;
+
HInstruction target = new HStatic(element);
add(target);
var inputs = <HInstruction>[];
@@ -2617,7 +2704,11 @@
visit(node.expression);
value = pop();
}
- close(attachPosition(new HReturn(value), node)).addSuccessor(graph.exit);
+ if (!inliningStack.isEmpty()) {
+ localsHandler.updateLocal(returnElement, value);
+ } else {
+ close(attachPosition(new HReturn(value), node)).addSuccessor(graph.exit);
+ }
}
visitThrow(Throw node) {
@@ -3421,6 +3512,87 @@
}
}
+/**
+ * This class visits the method that is a candidate for inlining and
+ * finds whether it is too difficult to inline.
+ */
+class InlineWeeder extends AbstractVisitor {
+ final TreeElements elements;
+ bool seenReturn = false;
+ bool tooDifficult = false;
+
+ InlineWeeder(this.elements);
+
+ static bool canBeInlined(FunctionExpression functionExpression,
+ TreeElements elements) {
+ InlineWeeder weeder = new InlineWeeder(elements);
+ weeder.visit(functionExpression.body);
+ if (weeder.tooDifficult) return false;
+ return true;
+ }
+
+ void visit(Node node) {
+ node.accept(this);
+ }
+
+ void visitNode(Node node) {
+ if (seenReturn) {
+ tooDifficult = true;
+ } else {
+ node.visitChildren(this);
+ }
+ }
+
+ void visitFunctionExpression(Node node) {
+ tooDifficult = true;
+ }
+
+ void visitFunctionDeclaration(Node node) {
+ tooDifficult = true;
+ }
+
+ void visitSend(Node node) {
+ Element element = elements[node];
+ // Native methods rely on the names of the arguments. If we inline they
+ // could change.
+ if (!Element.isInvalid(element) && element.kind == ElementKind.FOREIGN) {
+ tooDifficult = true;
+ } else {
+ node.visitChildren(this);
+ }
+ }
+
+ visitLoop(Node node) {
+ node.visitChildren(this);
+ if (seenReturn) tooDifficult = true;
+ }
+
+ void visitReturn(Node node) {
+ if (seenReturn || node.getBeginToken().stringValue === 'native') {
+ tooDifficult = true;
+ return;
+ }
+ node.visitChildren(this);
+ seenReturn = true;
+ }
+
+ void visitTryStatement(Node node) {
+ tooDifficult = true;
+ }
+}
+
+class InliningState {
+ final PartialFunctionElement function;
+ final Element oldReturnElement;
+ final TreeElements oldElements;
+ final List<HInstruction> oldStack;
+
+ InliningState(this.function,
+ this.oldReturnElement,
+ this.oldElements,
+ this.oldStack);
+}
+
class SsaBranch {
final SsaBranchBuilder branchBuilder;
final HBasicBlock block;

Powered by Google App Engine
This is Rietveld 408576698