-
Notifications
You must be signed in to change notification settings - Fork 858
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add runtime optimizations for math operations
This adds a number of special-purpose linkers for invoke dynamic instructions that let us take a shorter code path for many math operations and a few other things, including: * "==", "===", and "+" operations * Other math operations like conversions * Access to the "prototype" field of functions * Access to the "length" field of native arrays
- Loading branch information
Showing
11 changed files
with
932 additions
and
34 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
64 changes: 64 additions & 0 deletions
64
rhino/src/main/java/org/mozilla/javascript/optimizer/BaseFunctionLinker.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
package org.mozilla.javascript.optimizer; | ||
|
||
import java.lang.invoke.MethodHandle; | ||
import java.lang.invoke.MethodHandles; | ||
import java.lang.invoke.MethodType; | ||
import jdk.dynalink.StandardNamespace; | ||
import jdk.dynalink.StandardOperation; | ||
import jdk.dynalink.linker.GuardedInvocation; | ||
import jdk.dynalink.linker.LinkRequest; | ||
import jdk.dynalink.linker.LinkerServices; | ||
import jdk.dynalink.linker.TypeBasedGuardingDynamicLinker; | ||
import jdk.dynalink.linker.support.Guards; | ||
import org.mozilla.javascript.BaseFunction; | ||
import org.mozilla.javascript.Context; | ||
import org.mozilla.javascript.Scriptable; | ||
|
||
/** | ||
* This linker optimizes accesses to the "prototype" property of any standard Rhino function so that | ||
* it calls the native function rather than going through' a property name match. | ||
*/ | ||
@SuppressWarnings("AndroidJdkLibsChecker") | ||
class BaseFunctionLinker implements TypeBasedGuardingDynamicLinker { | ||
@Override | ||
public boolean canLinkType(Class<?> type) { | ||
return BaseFunction.class.isAssignableFrom(type); | ||
} | ||
|
||
@Override | ||
public GuardedInvocation getGuardedInvocation(LinkRequest req, LinkerServices svc) | ||
throws Exception { | ||
if (req.isCallSiteUnstable()) { | ||
return null; | ||
} | ||
|
||
ParsedOperation op = new ParsedOperation(req.getCallSiteDescriptor().getOperation()); | ||
MethodHandle mh = null; | ||
MethodHandle guard = null; | ||
|
||
if (op.isNamespace(StandardNamespace.PROPERTY)) { | ||
if (op.isOperation(StandardOperation.GET, RhinoOperation.GETNOWARN) | ||
&& "prototype".equals(op.getName())) { | ||
MethodHandles.Lookup lookup = MethodHandles.lookup(); | ||
MethodType mType = req.getCallSiteDescriptor().getMethodType(); | ||
mh = lookup.findStatic(BaseFunctionLinker.class, "getPrototype", mType); | ||
guard = Guards.getInstanceOfGuard(BaseFunction.class); | ||
} | ||
} | ||
|
||
if (mh != null) { | ||
assert guard != null; | ||
if (DefaultLinker.DEBUG) { | ||
System.out.println(op + " base function operation"); | ||
} | ||
return new GuardedInvocation(mh, guard); | ||
} | ||
|
||
return null; | ||
} | ||
|
||
@SuppressWarnings("unused") | ||
private static Object getPrototype(Object o, Context cx, Scriptable scope) { | ||
return ((BaseFunction) o).getPrototypeProperty(); | ||
} | ||
} |
74 changes: 74 additions & 0 deletions
74
rhino/src/main/java/org/mozilla/javascript/optimizer/BooleanLinker.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
package org.mozilla.javascript.optimizer; | ||
|
||
import java.lang.invoke.MethodHandle; | ||
import java.lang.invoke.MethodHandles; | ||
import java.lang.invoke.MethodType; | ||
import java.util.Objects; | ||
import jdk.dynalink.linker.GuardedInvocation; | ||
import jdk.dynalink.linker.LinkRequest; | ||
import jdk.dynalink.linker.LinkerServices; | ||
import jdk.dynalink.linker.TypeBasedGuardingDynamicLinker; | ||
import jdk.dynalink.linker.support.Guards; | ||
|
||
/** | ||
* This linker short-circuits invocations of "==", "===", and "toBoolean" operations when the | ||
* argument is already a boolean. | ||
*/ | ||
@SuppressWarnings("AndroidJdkLibsChecker") | ||
class BooleanLinker implements TypeBasedGuardingDynamicLinker { | ||
@Override | ||
public boolean canLinkType(Class<?> type) { | ||
return Boolean.class.equals(type); | ||
} | ||
|
||
@Override | ||
public GuardedInvocation getGuardedInvocation(LinkRequest req, LinkerServices svc) | ||
throws Exception { | ||
if (req.isCallSiteUnstable()) { | ||
return null; | ||
} | ||
|
||
ParsedOperation op = new ParsedOperation(req.getCallSiteDescriptor().getOperation()); | ||
MethodHandle mh = null; | ||
MethodHandle guard = null; | ||
|
||
if (op.isNamespace(RhinoNamespace.MATH)) { | ||
MethodHandles.Lookup lookup = MethodHandles.lookup(); | ||
MethodType mType = req.getCallSiteDescriptor().getMethodType(); | ||
|
||
if (op.isOperation(RhinoOperation.EQ, RhinoOperation.SHALLOWEQ) | ||
&& req.getArguments()[1] instanceof Boolean) { | ||
mh = lookup.findStatic(BooleanLinker.class, "eq", mType); | ||
guard = lookup.findStatic(BooleanLinker.class, "testEq", mType); | ||
} else if (op.isOperation(RhinoOperation.TOBOOLEAN)) { | ||
mh = lookup.findStatic(BooleanLinker.class, "toBoolean", mType); | ||
guard = Guards.getInstanceOfGuard(Boolean.class); | ||
} | ||
} | ||
|
||
if (mh != null) { | ||
assert guard != null; | ||
if (DefaultLinker.DEBUG) { | ||
System.out.println(op + " boolean operation"); | ||
} | ||
return new GuardedInvocation(mh, guard); | ||
} | ||
|
||
return null; | ||
} | ||
|
||
@SuppressWarnings("unused") | ||
private static boolean testEq(Object lval, Object rval) { | ||
return lval instanceof Boolean && rval instanceof Boolean; | ||
} | ||
|
||
@SuppressWarnings("unused") | ||
private static boolean eq(Object lval, Object rval) { | ||
return Objects.equals(lval, rval); | ||
} | ||
|
||
@SuppressWarnings("unused") | ||
private static boolean toBoolean(Object raw) { | ||
return ((Boolean) raw); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.