Skip to content

Commit

Permalink
Add INDY ops for math operations
Browse files Browse the repository at this point in the history
This will let us optimize them later, since most math operations are
long chains of "instanceof" checks.
  • Loading branch information
gbrail committed Oct 25, 2024
1 parent e9592d7 commit c588025
Show file tree
Hide file tree
Showing 8 changed files with 301 additions and 130 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,8 +33,11 @@ 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();
// Check the list of type-based linkers first, and fall back to the
// default linker, which will always work by falling back to
// generic ScriptRuntime methods.
factory.setPrioritizedLinkers(new ConstAwareLinker(), new DefaultLinker());
linker = factory.createLinker();
}
Expand All @@ -44,7 +47,7 @@ public class Bootstrapper {
public static CallSite bootstrap(MethodHandles.Lookup lookup, String name, MethodType mType)
throws NoSuchMethodException, IllegalAccessException {
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 +140,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 c588025

Please sign in to comment.