/** @see #resolveInlineConflicts */ private void resolveInlineConflictsForFunction(FunctionState fs) { // Functions that aren't referenced don't cause conflicts. if (!fs.hasReferences() || !fs.canInline()) { return; } Node fnNode = fs.getFn().getFunctionNode(); Set<String> names = findCalledFunctions(fnNode); if (!names.isEmpty()) { // Prevent the removal of the referenced functions. for (String name : names) { FunctionState fsCalled = fns.get(name); if (fsCalled != null && fsCalled.canRemove()) { fsCalled.setRemove(false); // For functions that can no longer be removed, check if they should // still be inlined. if (!mimimizeCost(fsCalled)) { // It can't be inlined remove it from the list. fsCalled.setInline(false); } } } // Make a copy of the Node, so it isn't changed by other inlines. fs.setSafeFnNode(fs.getFn().getFunctionNode().cloneTree()); } }
private boolean maybeAddReferenceUsingMode( NodeTraversal t, FunctionState fs, Node callNode, JSModule module, InliningMode mode) { // If many functions are inlined into the same function F in the same // inlining round, then the size of F may exceed the max size. // This could be avoided if we bail later, during the inlining phase, eg, // in Inline#visitCallSite. However, that is not safe, because at that // point expression decomposition has already run, and we want to // decompose expressions only for the calls that are actually inlined. if (enforceMaxSizeAfterInlining && targetSizeAfterInlineExceedsLimit(t, fs)) { return false; } Reference candidate = new Reference(callNode, t.getScope(), module, mode); CanInlineResult result = injector.canInlineReferenceToFunction( candidate, fs.getFn().getFunctionNode(), fs.getNamesToAlias(), fs.getReferencesThis(), fs.hasInnerFunctions()); if (result != CanInlineResult.NO) { // Yeah! candidate.setRequiresDecomposition(result == CanInlineResult.AFTER_PREPARATION); fs.addReference(candidate); return true; } return false; }
private boolean maybeAddReferenceUsingMode( NodeTraversal t, FunctionState fs, Node callNode, JSModule module, InliningMode mode) { if (specializationState != null) { // If we're specializing, make sure we can fixup // the containing function before inlining Node containingFunction = getContainingFunction(t); if (containingFunction != null && !specializationState.canFixupFunction(containingFunction)) { return false; } } CanInlineResult result = injector.canInlineReferenceToFunction( t, callNode, fs.getFn().getFunctionNode(), fs.getNamesToAlias(), mode, fs.getReferencesThis(), fs.hasInnerFunctions()); if (result != CanInlineResult.NO) { // Yeah! boolean decompose = (result == CanInlineResult.AFTER_PREPARATION); fs.addReference(new Reference(callNode, module, mode, decompose)); return true; } return false; }
/** @return Whether inlining the function reduces code size. */ private boolean inliningLowersCost(FunctionState fs) { return injector.inliningLowersCost( fs.getModule(), fs.getFn().getFunctionNode(), fs.getReferences(), fs.getNamesToAlias(), fs.canRemove(), fs.getReferencesThis()); }
/** Inline a function into the call site. */ private void inlineFunction(NodeTraversal t, Reference ref, FunctionState fs) { Function fn = fs.getFn(); String fnName = fn.getName(); Node fnNode = fs.getSafeFnNode(); Node newExpr = injector.inline(ref, fnName, fnNode); if (!newExpr.isEquivalentTo(ref.callNode)) { t.getCompiler().reportChangeToEnclosingScope(newExpr); } t.getCompiler().addToDebugLog("Inlined function: " + fn.getName()); }
/** Removed inlined functions that no longer have any references. */ void removeInlinedFunctions() { for (FunctionState fs : fns.values()) { if (fs.canRemove()) { Function fn = fs.getFn(); Preconditions.checkState(fs.canInline()); Preconditions.checkState(fn != null); verifyAllReferencesInlined(fs); fn.remove(); } } }
/** Removed inlined functions that no longer have any references. */ void removeInlinedFunctions() { for (FunctionState fs : fns.values()) { if (fs.canRemove()) { Function fn = fs.getFn(); Preconditions.checkState(fs.canInline()); Preconditions.checkState(fn != null); verifyAllReferencesInlined(fs); if (specializationState != null) { specializationState.reportRemovedFunction(fn.getFunctionNode(), fn.getDeclaringBlock()); } fn.remove(); } } }
private boolean targetSizeAfterInlineExceedsLimit(NodeTraversal t, FunctionState fs) { Node containingFunction = getContainingFunction(t); // Always inline at the top level, // unless maybeAddFunction has marked fs as not inlinable. if (containingFunction == null) { return false; } Node inlinedFun = fs.getFn().getFunctionNode(); if (isAlwaysInlinable(inlinedFun)) { return false; } int inlinedFunSize = NodeUtil.countAstSizeUpToLimit(NodeUtil.getFunctionBody(inlinedFun), maxSizeAfterInlining); int targetFunSize = NodeUtil.countAstSizeUpToLimit(containingFunction, maxSizeAfterInlining); return inlinedFunSize + targetFunSize > maxSizeAfterInlining; }