Am I right that the goal of this would be to allow for specific Ruby
calls to be sped up by skipping a lot of the dynamic nature of the
language? Assuming that's true, what I was hoping for would be a
non-invasive way of adding functionality like to without breaking my
ability to still run the code on MRI. In my ideal world I would be able
to add "hints" to JRuby to speed things up in a normally unsafe way, but
not have to change the actual code so that if I still wanted to run on
MRI I could, these special optimizations would just not be used. Maybe
something in a comment?
Joe
On Sun, Apr 12, 2009 at 2:15 AM, Charles Oliver Nutter
<[email protected] <mailto:[email protected]>> wrote:
Attached is a patch that adds the ability to make *static* calls in
compiled code. Basically the compiler sees it and makes it a static
invocation instead of a dynamic one. It's pretty ugly though, since
you need to specify all the Java signature stuff:
To make "n - 2" into a static call against RubyFixnum, you'd use:
__static_call__("org/jruby/RubyFixnum", n, "op_minus",
"(Lorg/jruby/runtime/ThreadContext;Lorg/jruby/runtime/builtin/IRubyObject;)Lorg/jruby/runtime/builtin/IRubyObject;",
2)
The compiler would then evaluate n, cast it to RubyFixnum, evaluate
the arguments, and make the call directly.
A better syntax would probably make this more usable, but in my
testing I have not seen it make a substantial performance
difference. Our call pipeline, once compiled, appears to be pretty
darn good.
At any rate, the patch is attached for posterity. I'd love to hear
about other ideas for this.
- Charlie
diff --git a/src/org/jruby/compiler/ASTCompiler.java
b/src/org/jruby/compiler/ASTCompiler.java
index 5a75698..5597062 100644
--- a/src/org/jruby/compiler/ASTCompiler.java
+++ b/src/org/jruby/compiler/ASTCompiler.java
@@ -2120,6 +2120,11 @@ public class ASTCompiler {
public void compileFCall(Node node, BodyCompiler context,
boolean expr) {
final FCallNode fcallNode = (FCallNode) node;
+ if (fcallNode.getName().equals("__static_call__")) {
+ compileStaticCall(fcallNode, context, expr);
+ return;
+ }
+
ArgumentsCallback argsCallback =
getArgsCallback(fcallNode.getArgsNode());
CompilerCallback closureArg = getBlock(fcallNode.getIterNode());
@@ -2129,6 +2134,35 @@ public class ASTCompiler {
if (!expr) context.consumeCurrentValue();
}
+ public void compileStaticCall(FCallNode fcallNode, BodyCompiler
context, boolean expr) {
+ ArrayNode args = (ArrayNode)fcallNode.getArgsNode();
+
+ // args[0] is the Java type to cast to
+ StrNode type = (StrNode)args.get(0);
+
+ // args[1] is the receiver node
+ final Node receiver = args.get(1);
+
+ // args[2] is the method name
+ StrNode method = (StrNode)args.get(2);
+
+ // args[3] is the signature
+ StrNode signature = (StrNode)args.get(3);
+
+ // args[4] is the expression to produce all arguments
+ final Node callArgs = args.get(4);
+
+ CompilerCallback receiverCallback = new CompilerCallback() {
+ public void call(BodyCompiler context) {
+ compile(receiver, context, true);
+ }
+ };
+
+ ArgumentsCallback argsCallback = getArgsCallback(callArgs);
+
+ context.compileStaticCall(receiverCallback, argsCallback,
type.getValue().toString(), method.getValue().toString(),
signature.getValue().toString());
+ }
+
private CompilerCallback getBlock(Node node) {
if (node == null) {
return null;
@@ -2543,27 +2577,23 @@ public class ASTCompiler {
public void compileIter(Node node, BodyCompiler context) {
final IterNode iterNode = (IterNode) node;
- // create the closure class and instantiate it
final CompilerCallback closureBody = new CompilerCallback() {
+ public void call(BodyCompiler context) {
+ if (iterNode.getBodyNode() != null) {
+ compile(iterNode.getBodyNode(), context, true);
+ } else {
+ context.loadNil();
+ }
+ }
+ };
- public void call(BodyCompiler context) {
- if (iterNode.getBodyNode() != null) {
- compile(iterNode.getBodyNode(),
context, true);
- } else {
- context.loadNil();
- }
- }
- };
-
- // create the closure class and instantiate it
final CompilerCallback closureArgs = new CompilerCallback() {
-
- public void call(BodyCompiler context) {
- if (iterNode.getVarNode() != null) {
-
compileAssignment(iterNode.getVarNode(), context, false);
- }
- }
- };
+ public void call(BodyCompiler context) {
+ if (iterNode.getVarNode() != null) {
+ compileAssignment(iterNode.getVarNode(),
context, false);
+ }
+ }
+ };
boolean hasMultipleArgsHead = false;
if (iterNode.getVarNode() instanceof MultipleAsgnNode) {
diff --git a/src/org/jruby/compiler/BodyCompiler.java
b/src/org/jruby/compiler/BodyCompiler.java
index 94f501d..65d3213 100644
--- a/src/org/jruby/compiler/BodyCompiler.java
+++ b/src/org/jruby/compiler/BodyCompiler.java
@@ -587,4 +587,6 @@ public interface BodyCompiler {
List<ArgumentsCallback> conditionals,
List<CompilerCallback> bodies,
CompilerCallback fallback);
+
+ public void compileStaticCall(CompilerCallback receiver,
ArgumentsCallback args, String type, String method, String signature);
}
diff --git a/src/org/jruby/compiler/impl/BaseBodyCompiler.java
b/src/org/jruby/compiler/impl/BaseBodyCompiler.java
index 56ab7d2..63fc794 100644
--- a/src/org/jruby/compiler/impl/BaseBodyCompiler.java
+++ b/src/org/jruby/compiler/impl/BaseBodyCompiler.java
@@ -2532,4 +2532,12 @@ public abstract class BaseBodyCompiler
implements BodyCompiler {
getVariableCompiler().releaseTempLocal();
}
+
+ public void compileStaticCall(CompilerCallback receiver,
ArgumentsCallback args, String type, String methodName, String
signature) {
+ receiver.call(this);
+ method.checkcast(type);
+ if
(signature.startsWith("(Lorg/jruby/runtime/ThreadContext;"))
loadThreadContext();
+ args.call(this);
+ method.invokevirtual(type, methodName, signature);
+ }
}
\ No newline at end of file
---------------------------------------------------------------------
To unsubscribe from this list, please visit:
http://xircles.codehaus.org/manage_email