# HG changeset patch # User attila # Date 1413799596 -7200 # Node ID 03c06c337d9d296946bde9c71a3b9c4bc74c160b # Parent 8c51767d534d8d5ec83506352a600733241b4ee0 8059844: Implement optimistic splitter Reviewed-by: hannesw, lagergren diff -r 8c51767d534d -r 03c06c337d9d src/jdk/nashorn/internal/codegen/ApplySpecialization.java --- a/src/jdk/nashorn/internal/codegen/ApplySpecialization.java Fri Oct 17 14:24:26 2014 +0200 +++ b/src/jdk/nashorn/internal/codegen/ApplySpecialization.java Mon Oct 20 12:06:36 2014 +0200 @@ -27,6 +27,7 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS_VAR; import static jdk.nashorn.internal.codegen.CompilerConstants.EXPLODED_ARGUMENT_PREFIX; + import java.lang.invoke.MethodType; import java.util.ArrayDeque; import java.util.ArrayList; @@ -38,6 +39,7 @@ import jdk.nashorn.internal.ir.CallNode; import jdk.nashorn.internal.ir.Expression; import jdk.nashorn.internal.ir.FunctionNode; +import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.IdentNode; import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.Node; @@ -321,7 +323,7 @@ explodedArguments.pop(); - return newFunctionNode; + return newFunctionNode.setState(lc, CompilationState.BUILTINS_TRANSFORMED); } private static boolean isApply(final CallNode callNode) { diff -r 8c51767d534d -r 03c06c337d9d src/jdk/nashorn/internal/codegen/AssignSymbols.java --- a/src/jdk/nashorn/internal/codegen/AssignSymbols.java Fri Oct 17 14:24:26 2014 +0200 +++ b/src/jdk/nashorn/internal/codegen/AssignSymbols.java Mon Oct 20 12:06:36 2014 +0200 @@ -76,7 +76,6 @@ import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.RuntimeNode; import jdk.nashorn.internal.ir.RuntimeNode.Request; -import jdk.nashorn.internal.ir.SplitNode; import jdk.nashorn.internal.ir.Statement; import jdk.nashorn.internal.ir.SwitchNode; import jdk.nashorn.internal.ir.Symbol; @@ -135,9 +134,6 @@ if (!(functionNode.hasScopeBlock() || functionNode.needsParentScope())) { functionNode.compilerConstant(SCOPE).setNeedsSlot(false); } - if (!functionNode.usesReturnSymbol()) { - functionNode.compilerConstant(RETURN).setNeedsSlot(false); - } // Named function expressions that end up not referencing themselves won't need a local slot for the self symbol. if(!functionNode.isDeclared() && !functionNode.usesSelfSymbol() && !functionNode.isAnonymous()) { final Symbol selfSymbol = functionNode.getBody().getExistingSymbol(functionNode.getIdent().getName()); @@ -1014,7 +1010,7 @@ boolean previousWasBlock = false; for (final Iterator it = lc.getAllNodes(); it.hasNext();) { final LexicalContextNode node = it.next(); - if (node instanceof FunctionNode || node instanceof SplitNode || isSplitArray(node)) { + if (node instanceof FunctionNode || isSplitArray(node)) { // We reached the function boundary or a splitting boundary without seeing a definition for the symbol. // It needs to be in scope. return true; diff -r 8c51767d534d -r 03c06c337d9d src/jdk/nashorn/internal/codegen/AstSerializer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk/nashorn/internal/codegen/AstSerializer.java Mon Oct 20 12:06:36 2014 +0200 @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.nashorn.internal.codegen; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.util.Collections; +import java.util.zip.Deflater; +import java.util.zip.DeflaterOutputStream; +import jdk.nashorn.internal.ir.Block; +import jdk.nashorn.internal.ir.FunctionNode; +import jdk.nashorn.internal.ir.LexicalContext; +import jdk.nashorn.internal.ir.Node; +import jdk.nashorn.internal.ir.Statement; +import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.runtime.options.Options; + +/** + * This static utility class performs serialization of FunctionNode ASTs to a byte array. + * The format is a standard Java serialization stream, deflated. + */ +final class AstSerializer { + // Experimentally, we concluded that compression level 4 gives a good tradeoff between serialization speed + // and size. + private static final int COMPRESSION_LEVEL = Options.getIntProperty("nashorn.serialize.compression", 4); + static byte[] serialize(final FunctionNode fn) { + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + try (final ObjectOutputStream oout = new ObjectOutputStream(new DeflaterOutputStream(out, + new Deflater(COMPRESSION_LEVEL)))) { + oout.writeObject(removeInnerFunctionBodies(fn)); + } catch (final IOException e) { + throw new AssertionError("Unexpected exception serializing function", e); + } + return out.toByteArray(); + } + + private static FunctionNode removeInnerFunctionBodies(final FunctionNode fn) { + return (FunctionNode)fn.accept(new NodeVisitor(new LexicalContext()) { + @Override + public Node leaveBlock(final Block block) { + if (lc.isFunctionBody() && lc.getFunction(block) != lc.getOutermostFunction()) { + return block.setStatements(lc, Collections.emptyList()); + } + return super.leaveBlock(block); + } + }); + } +} diff -r 8c51767d534d -r 03c06c337d9d src/jdk/nashorn/internal/codegen/ClassEmitter.java --- a/src/jdk/nashorn/internal/codegen/ClassEmitter.java Fri Oct 17 14:24:26 2014 +0200 +++ b/src/jdk/nashorn/internal/codegen/ClassEmitter.java Mon Oct 20 12:06:36 2014 +0200 @@ -51,6 +51,7 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor; import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor; import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup; + import java.io.ByteArrayOutputStream; import java.io.PrintWriter; import java.security.AccessController; @@ -64,7 +65,6 @@ import jdk.internal.org.objectweb.asm.util.TraceClassVisitor; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.FunctionNode; -import jdk.nashorn.internal.ir.SplitNode; import jdk.nashorn.internal.ir.debug.NashornClassReader; import jdk.nashorn.internal.ir.debug.NashornTextifier; import jdk.nashorn.internal.runtime.Context; @@ -476,12 +476,6 @@ methodsStarted.remove(method); } - SplitMethodEmitter method(final SplitNode splitNode, final String methodName, final Class rtype, final Class... ptypes) { - methodCount++; - methodNames.add(methodName); - return new SplitMethodEmitter(this, methodVisitor(EnumSet.of(Flag.PUBLIC, Flag.STATIC), methodName, rtype, ptypes), splitNode); - } - /** * Add a new method to the class - defaults to public method * diff -r 8c51767d534d -r 03c06c337d9d src/jdk/nashorn/internal/codegen/CodeGenerator.java --- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java Fri Oct 17 14:24:26 2014 +0200 +++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java Mon Oct 20 12:06:36 2014 +0200 @@ -34,9 +34,7 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.GET_STRING; import static jdk.nashorn.internal.codegen.CompilerConstants.QUICK_PREFIX; import static jdk.nashorn.internal.codegen.CompilerConstants.REGEX_PREFIX; -import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN; import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE; -import static jdk.nashorn.internal.codegen.CompilerConstants.SPLIT_ARRAY_ARG; import static jdk.nashorn.internal.codegen.CompilerConstants.SPLIT_PREFIX; import static jdk.nashorn.internal.codegen.CompilerConstants.THIS; import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS; @@ -99,10 +97,10 @@ import jdk.nashorn.internal.ir.ForNode; import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.FunctionNode.CompilationState; +import jdk.nashorn.internal.ir.GetSplitState; import jdk.nashorn.internal.ir.IdentNode; import jdk.nashorn.internal.ir.IfNode; import jdk.nashorn.internal.ir.IndexNode; -import jdk.nashorn.internal.ir.JoinPredecessor; import jdk.nashorn.internal.ir.JoinPredecessorExpression; import jdk.nashorn.internal.ir.JumpStatement; import jdk.nashorn.internal.ir.LabelNode; @@ -121,7 +119,8 @@ import jdk.nashorn.internal.ir.ReturnNode; import jdk.nashorn.internal.ir.RuntimeNode; import jdk.nashorn.internal.ir.RuntimeNode.Request; -import jdk.nashorn.internal.ir.SplitNode; +import jdk.nashorn.internal.ir.SetSplitState; +import jdk.nashorn.internal.ir.SplitReturn; import jdk.nashorn.internal.ir.Statement; import jdk.nashorn.internal.ir.SwitchNode; import jdk.nashorn.internal.ir.Symbol; @@ -493,8 +492,7 @@ //walk up the chain from starting block and when we bump into the current function boundary, add the external //information. final FunctionNode fn = lc.getCurrentFunction(); - final int fnId = fn.getId(); - final int externalDepth = compiler.getScriptFunctionData(fnId).getExternalSymbolDepth(symbol.getName()); + final int externalDepth = compiler.getScriptFunctionData(fn.getId()).getExternalSymbolDepth(symbol.getName()); //count the number of scopes from this place to the start of the function @@ -1048,6 +1046,13 @@ } @Override + public boolean enterGetSplitState(final GetSplitState getSplitState) { + method.loadScope(); + method.invoke(Scope.GET_SPLIT_STATE); + return false; + } + + @Override public boolean enterDefault(final Node otherNode) { // Must have handled all expressions that can legally be encountered. throw new AssertionError(otherNode.getClass().getName()); @@ -1219,7 +1224,7 @@ popScopesUntil(target); final Label targetLabel = jump.getTargetLabel(target); targetLabel.markAsBreakTarget(); - method.splitAwareGoto(lc, targetLabel, target); + method._goto(targetLabel); return false; } @@ -2029,10 +2034,10 @@ } private void lineNumber(final int lineNumber) { - if (lineNumber != lastLineNumber) { + if (lineNumber != lastLineNumber && lineNumber != Node.NO_LINE_NUMBER) { method.lineNumber(lineNumber); - } - lastLineNumber = lineNumber; + lastLineNumber = lineNumber; + } } int getLastLineNumber() { @@ -2079,13 +2084,14 @@ method.begin(); defineCommonSplitMethodParameters(); - defineSplitMethodParameter(3, arrayType); - - fixScopeSlot(currentFunction); + defineSplitMethodParameter(CompilerConstants.SPLIT_ARRAY_ARG.slot(), arrayType); + + // NOTE: when this is no longer needed, SplitIntoFunctions will no longer have to add IS_SPLIT + // to synthetic functions, and FunctionNode.needsCallee() will no longer need to test for isSplit(). + final int arraySlot = fixScopeSlot(currentFunction, 3); lc.enterSplitNode(); - final int arraySlot = SPLIT_ARRAY_ARG.slot(); for (int i = arrayUnit.getLo(); i < arrayUnit.getHi(); i++) { method.load(arrayType, arraySlot); storeElement(nodes, elementType, postsets[i]); @@ -2700,73 +2706,6 @@ method.convert(newRuntimeNode.getType()); } - @Override - public boolean enterSplitNode(final SplitNode splitNode) { - if(!method.isReachable()) { - return false; - } - - final CompileUnit splitCompileUnit = splitNode.getCompileUnit(); - - final FunctionNode fn = lc.getCurrentFunction(); - final String className = splitCompileUnit.getUnitClassName(); - final String name = splitNode.getName(); - - final Type returnType = fn.getReturnType(); - - final Class rtype = fn.getReturnType().getTypeClass(); - final boolean needsArguments = fn.needsArguments(); - final Class[] ptypes = needsArguments ? - new Class[] {ScriptFunction.class, Object.class, ScriptObject.class, ScriptObject.class} : - new Class[] {ScriptFunction.class, Object.class, ScriptObject.class}; - - final MethodEmitter caller = method; - unit = lc.pushCompileUnit(splitCompileUnit); - - final Call splitCall = staticCallNoLookup( - className, - name, - methodDescriptor(rtype, ptypes)); - - final MethodEmitter splitEmitter = - splitCompileUnit.getClassEmitter().method( - splitNode, - name, - rtype, - ptypes); - - pushMethodEmitter(splitEmitter); - method.setFunctionNode(fn); - - assert fn.needsCallee() : "split function should require callee"; - caller.loadCompilerConstant(CALLEE); - caller.loadCompilerConstant(THIS); - caller.loadCompilerConstant(SCOPE); - if (needsArguments) { - caller.loadCompilerConstant(ARGUMENTS); - } - caller.invoke(splitCall); - caller.storeCompilerConstant(RETURN, returnType); - - method.begin(); - - defineCommonSplitMethodParameters(); - if(needsArguments) { - defineSplitMethodParameter(3, ARGUMENTS); - } - - // Copy scope to its target slot as first thing because the original slot could be used by return symbol. - fixScopeSlot(fn); - - final int returnSlot = fn.compilerConstant(RETURN).getSlot(returnType); - method.defineBlockLocalVariable(returnSlot, returnSlot + returnType.getSlots()); - method.loadUndefined(returnType); - method.storeCompilerConstant(RETURN, returnType); - - lc.enterSplitNode(); - return true; - } - private void defineCommonSplitMethodParameters() { defineSplitMethodParameter(0, CALLEE); defineSplitMethodParameter(1, THIS); @@ -2782,114 +2721,40 @@ method.onLocalStore(type, slot); } - private void fixScopeSlot(final FunctionNode functionNode) { + private int fixScopeSlot(final FunctionNode functionNode, final int extraSlot) { // TODO hack to move the scope to the expected slot (needed because split methods reuse the same slots as the root method) final int actualScopeSlot = functionNode.compilerConstant(SCOPE).getSlot(SCOPE_TYPE); final int defaultScopeSlot = SCOPE.slot(); + int newExtraSlot = extraSlot; if (actualScopeSlot != defaultScopeSlot) { - method.defineBlockLocalVariable(actualScopeSlot, actualScopeSlot + 1); + if (actualScopeSlot == extraSlot) { + newExtraSlot = extraSlot + 1; + method.defineBlockLocalVariable(newExtraSlot, newExtraSlot + 1); + method.load(Type.OBJECT, extraSlot); + method.storeHidden(Type.OBJECT, newExtraSlot); + } else { + method.defineBlockLocalVariable(actualScopeSlot, actualScopeSlot + 1); + } method.load(SCOPE_TYPE, defaultScopeSlot); method.storeCompilerConstant(SCOPE); } + return newExtraSlot; } @Override - public Node leaveSplitNode(final SplitNode splitNode) { - assert method instanceof SplitMethodEmitter; - lc.exitSplitNode(); - final boolean hasReturn = method.hasReturn(); - final SplitMethodEmitter splitMethod = ((SplitMethodEmitter)method); - final List