Skip to content

Commit

Permalink
Add INDY ops for math operations (#1711)
Browse files Browse the repository at this point in the history
* Add INDY ops for math operations

This will let us optimize them later, since most math operations are
long chains of "instanceof" checks.
  • Loading branch information
gbrail authored Oct 30, 2024
1 parent d475d06 commit 838b6ca
Show file tree
Hide file tree
Showing 8 changed files with 301 additions and 131 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1195,7 +1195,7 @@ private void generateExpression(Node node, Node parent) {
{
generateExpression(child, node);
cfw.add(ByteCode.DUP);
addScriptRuntimeInvoke("toBoolean", "(Ljava/lang/Object;)Z");
addDynamicInvoke("MATH:TOBOOLEAN", Signatures.MATH_TO_BOOLEAN);
int falseTarget = cfw.acquireLabel();
if (type == Token.AND) cfw.add(ByteCode.IFEQ, falseTarget);
else cfw.add(ByteCode.IFNE, falseTarget);
Expand All @@ -1210,7 +1210,7 @@ private void generateExpression(Node node, Node parent) {
Node ifThen = child.getNext();
Node ifElse = ifThen.getNext();
generateExpression(child, node);
addScriptRuntimeInvoke("toBoolean", "(Ljava/lang/Object;)Z");
addDynamicInvoke("MATH:TOBOOLEAN", Signatures.MATH_TO_BOOLEAN);
int elseTarget = cfw.acquireLabel();
cfw.add(ByteCode.IFEQ, elseTarget);
int stack = cfw.getStackTop();
Expand Down Expand Up @@ -1245,12 +1245,7 @@ private void generateExpression(Node node, Node parent) {
break;
default:
cfw.addALoad(contextLocal);
addScriptRuntimeInvoke(
"add",
"(Ljava/lang/Object;"
+ "Ljava/lang/Object;"
+ "Lorg/mozilla/javascript/Context;"
+ ")Ljava/lang/Object;");
addDynamicInvoke("MATH:ADD", Signatures.MATH_ADD);
}
}
break;
Expand Down Expand Up @@ -1928,7 +1923,7 @@ private void generateIfJump(Node node, Node parent, int trueLabel, int falseLabe
default:
// Generate generic code for non-optimized jump
generateExpression(node, parent);
addScriptRuntimeInvoke("toBoolean", "(Ljava/lang/Object;)Z");
addDynamicInvoke("MATH:TOBOOLEAN", Signatures.MATH_TO_BOOLEAN);
cfw.add(ByteCode.IFNE, trueLabel);
cfw.add(ByteCode.GOTO, falseLabel);
}
Expand Down Expand Up @@ -2433,18 +2428,11 @@ private void visitStandardCall(Node node, Node child) {
// there are no checks for it
String name = child.getString();
if (isOptionalChainingCall) { // name?.()
// eval name and this and push name on stack
cfw.addPush(name);
cfw.addALoad(contextLocal);
// eval name and this and put name in dynamic signature just like
// with "GETWITHTHIS".
cfw.addALoad(variableObjectLocal);
addScriptRuntimeInvoke(
"getNameFunctionAndThisOptional",
""
+ "("
+ "Ljava/lang/String;"
+ "Lorg/mozilla/javascript/Context;"
+ "Lorg/mozilla/javascript/Scriptable;"
+ ")Lorg/mozilla/javascript/Callable;");
cfw.addALoad(contextLocal);
addDynamicInvoke("NAME:GETWITHTHISOPTIONAL:" + name, Signatures.NAME_GET_THIS);

// jump to afterLabel is name is not null and not undefined
afterLabel = cfw.acquireLabel();
Expand Down Expand Up @@ -3373,8 +3361,7 @@ private void visitSwitch(Jump switchNode, Node child) {
Node test = caseNode.getFirstChild();
generateExpression(test, caseNode);
cfw.addALoad(selector);
addScriptRuntimeInvoke(
"shallowEq", "(Ljava/lang/Object;" + "Ljava/lang/Object;" + ")Z");
addDynamicInvoke("MATH:SHALLOWEQ", Signatures.MATH_SHALLOW_EQ);
addGoto(caseNode.target, ByteCode.IFNE);
}
releaseWordLocal(selector);
Expand Down Expand Up @@ -3719,9 +3706,9 @@ private void visitBitOp(Node node, int type, Node child) {
// that we can return a 32-bit unsigned value, and call
// toUint32 instead of toInt32.
if (type == Token.URSH) {
addScriptRuntimeInvoke("toUint32", "(Ljava/lang/Object;)J");
addDynamicInvoke("MATH:TOUINT32", Signatures.MATH_TO_UINT32);
generateExpression(child.getNext(), node);
addScriptRuntimeInvoke("toInt32", "(Ljava/lang/Object;)I");
addDynamicInvoke("MATH:TOINT32", Signatures.MATH_TO_INT32);
// Looks like we need to explicitly mask the shift to 5 bits -
// LUSHR takes 6 bits.
cfw.addPush(31);
Expand Down Expand Up @@ -3916,8 +3903,25 @@ private void visitIfJumpRelOp(Node node, Node child, int trueGOTO, int falseGOTO
generateExpression(rChild, node);
}

cfw.addPush(type);
addScriptRuntimeInvoke("compare", "(Ljava/lang/Object;" + "Ljava/lang/Object;" + "I)Z");
String compareOp;
switch (type) {
case Token.GT:
compareOp = "MATH:COMPAREGT";
break;
case Token.LT:
compareOp = "MATH:COMPARELT";
break;
case Token.GE:
compareOp = "MATH:COMPAREGE";
break;
case Token.LE:
compareOp = "MATH:COMPARELE";
break;
default:
throw Kit.codeBug();
}

addDynamicInvoke(compareOp, Signatures.MATH_COMPARE);
cfw.add(ByteCode.IFNE, trueGOTO);
cfw.add(ByteCode.GOTO, falseGOTO);
}
Expand Down Expand Up @@ -3986,25 +3990,25 @@ private void visitIfJumpEqOp(Node node, Node child, int trueGOTO, int falseGOTO)
int testCode;
switch (type) {
case Token.EQ:
name = "eq";
name = "MATH:EQ";
testCode = ByteCode.IFNE;
break;
case Token.NE:
name = "eq";
name = "MATH:EQ";
testCode = ByteCode.IFEQ;
break;
case Token.SHEQ:
name = "shallowEq";
name = "MATH:SHALLOWEQ";
testCode = ByteCode.IFNE;
break;
case Token.SHNE:
name = "shallowEq";
name = "MATH:SHALLOWEQ";
testCode = ByteCode.IFEQ;
break;
default:
throw Codegen.badTree();
}
addScriptRuntimeInvoke(name, "(Ljava/lang/Object;" + "Ljava/lang/Object;" + ")Z");
addDynamicInvoke(name, Signatures.MATH_EQ);
cfw.add(testCode, trueGOTO);
cfw.add(ByteCode.GOTO, falseGOTO);
}
Expand Down Expand Up @@ -4269,7 +4273,7 @@ private void visitDotQuery(Node node, Node child) {
cfw.add(ByteCode.POP);

generateExpression(child.getNext(), node);
addScriptRuntimeInvoke("toBoolean", "(Ljava/lang/Object;)Z");
addDynamicInvoke("MATH:TOBOOLEAN", Signatures.MATH_TO_BOOLEAN);
cfw.addALoad(variableObjectLocal);
addScriptRuntimeInvoke(
"updateDotQuery",
Expand Down Expand Up @@ -4326,11 +4330,11 @@ private void addGoto(Node target, int jumpcode) {
}

private void addObjectToDouble() {
addScriptRuntimeInvoke("toNumber", "(Ljava/lang/Object;)D");
addDynamicInvoke("MATH:TONUMBER", Signatures.MATH_TO_NUMBER);
}

private void addObjectToNumeric() {
addScriptRuntimeInvoke("toNumeric", "(Ljava/lang/Object;)Ljava/lang/Number;");
addDynamicInvoke("MATH:TONUMERIC", Signatures.MATH_TO_NUMERIC);
}

private void addNewObjectArray(int size) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,20 @@ public class Bootstrapper {
private static final DynamicLinker linker;

static {
// Set up the linkers that will be invoked whenever a call site needs to be resolved.
// Set up the linkers
DynamicLinkerFactory factory = new DynamicLinkerFactory();
// The const-aware-linker will only bind a few operations, and everything
// else will fall back to the default linker, which will always bind.
factory.setPrioritizedLinkers(new ConstAwareLinker(), new DefaultLinker());
linker = factory.createLinker();
}

/** This is the method called by every call site in the bytecode to map it to a function. */
@SuppressWarnings("unused")
public static CallSite bootstrap(MethodHandles.Lookup lookup, String name, MethodType mType)
throws NoSuchMethodException, IllegalAccessException {
throws NoSuchMethodException {
Operation op = parseOperation(name);
// For now, use a very simple call site.
// ChainedCallSite lets a call site have a few options for complex situations
return linker.link(new ChainedCallSite(new CallSiteDescriptor(lookup, op, mType)));
}

Expand Down Expand Up @@ -137,6 +139,34 @@ private static Operation parseOperation(String name) throws NoSuchMethodExceptio
.withNamespace(RhinoNamespace.NAME)
.named(getNameSegment(tokens, name, 2));
}

} else if ("MATH".equals(namespaceName)) {
switch (opName) {
case "ADD":
return RhinoOperation.ADD.withNamespace(RhinoNamespace.MATH);
case "TOBOOLEAN":
return RhinoOperation.TOBOOLEAN.withNamespace(RhinoNamespace.MATH);
case "TOINT32":
return RhinoOperation.TOINT32.withNamespace(RhinoNamespace.MATH);
case "TOUINT32":
return RhinoOperation.TOUINT32.withNamespace(RhinoNamespace.MATH);
case "EQ":
return RhinoOperation.EQ.withNamespace(RhinoNamespace.MATH);
case "SHALLOWEQ":
return RhinoOperation.SHALLOWEQ.withNamespace(RhinoNamespace.MATH);
case "TONUMBER":
return RhinoOperation.TONUMBER.withNamespace(RhinoNamespace.MATH);
case "TONUMERIC":
return RhinoOperation.TONUMERIC.withNamespace(RhinoNamespace.MATH);
case "COMPAREGT":
return RhinoOperation.COMPARE_GT.withNamespace(RhinoNamespace.MATH);
case "COMPARELT":
return RhinoOperation.COMPARE_LT.withNamespace(RhinoNamespace.MATH);
case "COMPAREGE":
return RhinoOperation.COMPARE_GE.withNamespace(RhinoNamespace.MATH);
case "COMPARELE":
return RhinoOperation.COMPARE_LE.withNamespace(RhinoNamespace.MATH);
}
}

// Fall through to no match. This should only happen if the name in the bytecode
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import jdk.dynalink.NamedOperation;
import jdk.dynalink.NamespaceOperation;
import jdk.dynalink.Operation;
import jdk.dynalink.StandardNamespace;
import jdk.dynalink.StandardOperation;
import jdk.dynalink.linker.GuardedInvocation;
Expand All @@ -20,29 +17,19 @@
@SuppressWarnings("AndroidJdkLibsChecker")
class ConstAwareLinker implements GuardingDynamicLinker {
@Override
public GuardedInvocation getGuardedInvocation(LinkRequest req, LinkerServices svc)
throws Exception {
public GuardedInvocation getGuardedInvocation(LinkRequest req, LinkerServices svc) {
if (req.isCallSiteUnstable()) {
if (DefaultLinker.DEBUG) {
System.out.println(
req.getCallSiteDescriptor().getOperation() + ": unstable call site");
}
return null;
}

MethodHandles.Lookup lookup = MethodHandles.lookup();
Operation rootOp = req.getCallSiteDescriptor().getOperation();
MethodType mType = req.getCallSiteDescriptor().getMethodType();
String name = DefaultLinker.getName(rootOp);
Operation op = NamedOperation.getBaseOperation(rootOp);
ParsedOperation op = new ParsedOperation(req.getCallSiteDescriptor().getOperation());
Object target = req.getReceiver();

if (NamespaceOperation.contains(op, StandardOperation.GET, RhinoNamespace.NAME)
|| NamespaceOperation.contains(
op, StandardOperation.GET, StandardNamespace.PROPERTY)
|| NamespaceOperation.contains(
op, RhinoOperation.GETNOWARN, StandardNamespace.PROPERTY)) {
Object constValue = getConstValue(target, name);
if ((op.isNamespace(RhinoNamespace.NAME) && op.isOperation(StandardOperation.GET))
|| (op.isNamespace(StandardNamespace.PROPERTY)
&& op.isOperation(StandardOperation.GET, RhinoOperation.GETNOWARN))) {
Object constValue = getConstValue(target, op.getName());
if (constValue != null) {
// The guard returns boolean and compares the first argument to the
// target here. This works because the target is always our first argument.
Expand All @@ -55,7 +42,7 @@ public GuardedInvocation getGuardedInvocation(LinkRequest req, LinkerServices sv
0,
mType.parameterList());
if (DefaultLinker.DEBUG) {
System.out.println(rootOp + " constant");
System.out.println(op + ": constant");
}
return new GuardedInvocation(mh, guard);
}
Expand Down
Loading

0 comments on commit 838b6ca

Please sign in to comment.