Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: f1372e5e6153
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: bb3f5c0ee269
Choose a head ref
  • 2 commits
  • 4 files changed
  • 1 contributor

Commits on Jan 11, 2016

  1. Copy the full SHA
    0d2ae77 View commit details
  2. Improve block construction and yielding via invokedynamic.

    * Lazily create and cache fully-adapted handles for yielding.
    * Lazily create and cache test for yield GWT.
    * Start experimenting with Binding optimizations. This is disabled
      until invocation and block creation are done together, so we can
      see if the block's binding gets captured.
    * Fix a few issues with the yield logic.
    headius committed Jan 11, 2016
    Copy the full SHA
    bb3f5c0 View commit details
59 changes: 53 additions & 6 deletions core/src/main/java/org/jruby/ir/targets/Bootstrap.java
Original file line number Diff line number Diff line change
@@ -22,7 +22,6 @@
import org.jruby.runtime.Frame;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.Visibility;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.invokedynamic.GlobalSite;
import org.jruby.runtime.invokedynamic.MathLinker;
@@ -437,13 +436,13 @@ static MethodHandle buildNativeHandle(InvokeSite site, DynamicMethod method, boo
NativeCallMethod nativeMethod = (NativeCallMethod)method;
DynamicMethod.NativeCall nativeCall = nativeMethod.getNativeCall();

int nativeArgCount = getNativeArgCount(method, nativeCall);

DynamicMethod.NativeCall nc = nativeCall;

if (nc.isJava()) {
// not supported yet, use DynamicMethod.call
} else {
int nativeArgCount = getNativeArgCount(method, nativeCall);

if (nativeArgCount >= 0) { // native methods only support arity 3
if (nativeArgCount == site.arity) {
// nothing to do
@@ -940,12 +939,60 @@ public static CallSite prepareBlock(Lookup lookup, String name, MethodType type,

CompiledIRBlockBody body = new CompiledIRBlockBody(bodyHandle, scope, encodedSignature);

return new ConstantCallSite(Binder.from(type).append(body).invokeStaticQuiet(lookup, Bootstrap.class, "prepareBlock"));
Binder binder = Binder.from(type);

binder = binder.fold(FRAME_SCOPE_BINDING);

// This optimization can't happen until we can see into the method we're calling to know if it reifies the block
if (false) {
if (scope.needsBinding()) {
if (scope.needsFrame()) {
binder = binder.fold(FRAME_SCOPE_BINDING);
} else {
binder = binder.fold(SCOPE_BINDING);
}
} else {
if (scope.needsFrame()) {
binder = binder.fold(FRAME_BINDING);
} else {
binder = binder.fold(SELF_BINDING);
}
}
}

MethodHandle blockMaker = binder.drop(1, 3)
.append(body)
.invoke(CONSTRUCT_BLOCK);

return new ConstantCallSite(blockMaker);
}

private static final Binder BINDING_MAKER_BINDER = Binder.from(Binding.class, ThreadContext.class, IRubyObject.class, DynamicScope.class);

private static final MethodHandle FRAME_SCOPE_BINDING = BINDING_MAKER_BINDER.invokeStaticQuiet(LOOKUP, Bootstrap.class, "frameScopeBinding");
public static Binding frameScopeBinding(ThreadContext context, IRubyObject self, DynamicScope scope) {
Frame frame = context.getCurrentFrame().capture();
return new Binding(self, frame, frame.getVisibility(), scope);
}

public static Block prepareBlock(ThreadContext context, IRubyObject self, DynamicScope scope, CompiledIRBlockBody body) throws Throwable {
Binding binding = context.currentBinding(self, scope);
private static final MethodHandle FRAME_BINDING = BINDING_MAKER_BINDER.invokeStaticQuiet(LOOKUP, Bootstrap.class, "frameBinding");
public static Binding frameBinding(ThreadContext context, IRubyObject self, DynamicScope scope) {
Frame frame = context.getCurrentFrame().capture();
return new Binding(self, frame, frame.getVisibility());
}

private static final MethodHandle SCOPE_BINDING = BINDING_MAKER_BINDER.invokeStaticQuiet(LOOKUP, Bootstrap.class, "scopeBinding");
public static Binding scopeBinding(ThreadContext context, IRubyObject self, DynamicScope scope) {
return new Binding(self, scope);
}

private static final MethodHandle SELF_BINDING = BINDING_MAKER_BINDER.invokeStaticQuiet(LOOKUP, Bootstrap.class, "selfBinding");
public static Binding selfBinding(ThreadContext context, IRubyObject self, DynamicScope scope) {
return new Binding(self);
}

private static final MethodHandle CONSTRUCT_BLOCK = Binder.from(Block.class, Binding.class, CompiledIRBlockBody.class).invokeStaticQuiet(LOOKUP, Bootstrap.class, "constructBlock");
public static Block constructBlock(Binding binding, CompiledIRBlockBody body) throws Throwable {
return new Block(body, binding);
}
}
67 changes: 10 additions & 57 deletions core/src/main/java/org/jruby/ir/targets/YieldSite.java
Original file line number Diff line number Diff line change
@@ -2,9 +2,7 @@

import com.headius.invokebinder.Binder;
import org.jruby.ir.runtime.IRRuntimeHelpers;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.Block;
import org.jruby.runtime.BlockBody;
import org.jruby.runtime.CompiledIRBlockBody;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
@@ -47,24 +45,12 @@ public static CallSite bootstrap(MethodHandles.Lookup lookup, String name, Metho
}

public IRubyObject yield(ThreadContext context, Block block, IRubyObject arg) throws Throwable {
MethodHandle handle = getHandleForBlock(block);

if (!unwrap && handle != null) {
MethodHandle test, target, fallback;

fallback = getTarget();

target = Binder.from(IRubyObject.class, ThreadContext.class, Block.class, IRubyObject.class)
.foldVoid(SET_NORMAL)
.filter(2, VALUE_TO_ARRAY)
.insert(2, block.getBody().getStaticScope())
.insert(3, IRubyObject.class, null)
.append(Block.class, Block.NULL_BLOCK)
.append(block.getBinding().getMethod())
.append(block.type)
.invoke(handle);
if (block.getBody() instanceof CompiledIRBlockBody) {
CompiledIRBlockBody compiledBody = (CompiledIRBlockBody) block.getBody();

test = Binder.from(boolean.class, ThreadContext.class, Block.class, IRubyObject.class).permute(1).append(handle).invoke(TEST_BLOCK);
MethodHandle target = unwrap ? compiledBody.getNormalYieldUnwrapHandle() : compiledBody.getNormalYieldHandle();
MethodHandle fallback = getTarget();
MethodHandle test = compiledBody.getTestBlockBody();

MethodHandle guard = MethodHandles.guardWithTest(test, target, fallback);

@@ -79,23 +65,12 @@ public IRubyObject yield(ThreadContext context, Block block, IRubyObject arg) th
}

public IRubyObject yieldSpecific(ThreadContext context, Block block) throws Throwable {
MethodHandle handle = getHandleForBlock(block);
if (handle != null) {
MethodHandle test, target, fallback;

fallback = getTarget();

target = Binder.from(IRubyObject.class, ThreadContext.class, Block.class)
.foldVoid(SET_NORMAL)
.append(block.getBody().getStaticScope())
.append(IRubyObject.class, null)
.append(IRubyObject[].class, null)
.append(Block.class, Block.NULL_BLOCK)
.append(block.getBinding().getMethod())
.append(block.type)
.invoke(handle);
if (block.getBody() instanceof CompiledIRBlockBody) {
CompiledIRBlockBody compiledBody = (CompiledIRBlockBody) block.getBody();

test = Binder.from(boolean.class, ThreadContext.class, Block.class).drop(0).append(handle).invoke(TEST_BLOCK);
MethodHandle target = compiledBody.getNormalYieldSpecificHandle();
MethodHandle fallback = getTarget();
MethodHandle test = compiledBody.getTestBlockBody();

MethodHandle guard = MethodHandles.guardWithTest(test, target, fallback);

@@ -108,26 +83,4 @@ public IRubyObject yieldSpecific(ThreadContext context, Block block) throws Thro

return IRRuntimeHelpers.yieldSpecific(context, block);
}

public static final MethodHandles.Lookup LOOKUP = MethodHandles.publicLookup();
private static final MethodHandle SET_NORMAL = Binder.from(void.class, ThreadContext.class, Block.class).drop(1).append(Block.Type.NORMAL).invokeVirtualQuiet(LOOKUP, "setCurrentBlockType");
// private static final MethodHandle YIELD_SPECIFIC = Binder.from(IRubyObject.class, ThreadContext.class, Block.class).invokeStaticQuiet(LOOKUP, IRRuntimeHelpers.class, "yieldSpecific");
// private static final MethodHandle YIELD = Binder.from(IRubyObject.class, ThreadContext.class, Block.class, IRubyObject.class, boolean.class).invokeStaticQuiet(LOOKUP, IRRuntimeHelpers.class, "yield");
private static final MethodHandle YIELD_SPECIFIC_FALLBACK = Binder.from(IRubyObject.class, YieldSite.class, ThreadContext.class, Block.class).invokeVirtualQuiet(LOOKUP, "yieldSpecific");
private static final MethodHandle YIELD_FALLBACK = Binder.from(IRubyObject.class, YieldSite.class, ThreadContext.class, Block.class, IRubyObject.class).invokeVirtualQuiet(LOOKUP, "yield");
private static final MethodHandle TEST_BLOCK = Binder.from(boolean.class, Block.class, MethodHandle.class).invokeStaticQuiet(LOOKUP, YieldSite.class, "testBlock");
private static final MethodHandle VALUE_TO_ARRAY = Binder.from(IRubyObject[].class, IRubyObject.class).invokeStaticQuiet(LOOKUP, IRRuntimeHelpers.class, "singleBlockArgToArray");

public static boolean testBlock(Block block, MethodHandle handle) {
return getHandleForBlock(block) == handle;
}

private static MethodHandle getHandleForBlock(Block block) {
BlockBody body = block.getBody();
if (block.getBody() instanceof CompiledIRBlockBody) {
CompiledIRBlockBody compiledBody = (CompiledIRBlockBody) body;
return compiledBody.getHandle();
}
return null;
}
}
28 changes: 26 additions & 2 deletions core/src/main/java/org/jruby/runtime/Binding.java
Original file line number Diff line number Diff line change
@@ -59,7 +59,7 @@ public class Binding {
/**
* frame of method which defined this block
*/
private final Frame frame;
private Frame frame;

public String method;
public String filename;
@@ -74,7 +74,7 @@ public class Binding {
/**
* A reference to all variable values (and names) that are in-scope for this block.
*/
private final DynamicScope dynamicScope;
private DynamicScope dynamicScope;

/**
* Binding-local scope for 1.9 mode.
@@ -124,6 +124,30 @@ public Binding(Frame frame, DynamicScope dynamicScope, String method, String fil
this.filename = filename;
this.line = line;
}

public Binding(IRubyObject self) {
this.self = self;
}

public Binding(IRubyObject self, Frame frame,
Visibility visibility) {
this.self = self;
this.frame = frame;
this.visibility = visibility;
}

public Binding(IRubyObject self, DynamicScope dynamicScope) {
this.self = self;
this.dynamicScope = dynamicScope;
}

public Binding(IRubyObject self, Frame frame,
Visibility visibility, DynamicScope dynamicScope) {
this.self = self;
this.frame = frame;
this.visibility = visibility;
this.dynamicScope = dynamicScope;
}

private Binding(Binding other) {
this(other.self, other.frame, other.visibility, other.dynamicScope, other.method, other.filename, other.line, other.dummyScope);
93 changes: 91 additions & 2 deletions core/src/main/java/org/jruby/runtime/CompiledIRBlockBody.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
package org.jruby.runtime;

import org.jruby.EvalType;
import org.jruby.ir.IRFlags;
import com.headius.invokebinder.Binder;
import org.jruby.ir.IRScope;
import org.jruby.ir.runtime.IRRuntimeHelpers;
import org.jruby.runtime.builtin.IRubyObject;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;

public class CompiledIRBlockBody extends IRBlockBody {
protected final MethodHandle handle;
protected volatile MethodHandle normalYieldSpecificHandle;
protected volatile MethodHandle normalYieldHandle;
protected volatile MethodHandle normalYieldUnwrapHandle;
protected volatile MethodHandle testBlockBody;

public CompiledIRBlockBody(MethodHandle handle, IRScope closure, long encodedSignature) {
super(closure, Signature.decode(encodedSignature));
@@ -19,6 +23,40 @@ public CompiledIRBlockBody(MethodHandle handle, IRScope closure, long encodedSig
closure.getStaticScope().determineModule();
}

private static final MethodHandle TEST_BLOCK_BODY = Binder.from(boolean.class, Block.class, IRBlockBody.class).invokeStaticQuiet(MethodHandles.lookup(), CompiledIRBlockBody.class, "testBlockBody");

public static boolean testBlockBody(Block block, IRBlockBody body) {
return block.getBody() == body;
}

private static final MethodHandle FOLD_METHOD1 = Binder.from(String.class, ThreadContext.class, Block.class).invokeStaticQuiet(MethodHandles.lookup(), CompiledIRBlockBody.class, "foldMethod");
private static String foldMethod(ThreadContext context, Block block) {
return block.getBinding().getMethod();
}

private static final MethodHandle FOLD_TYPE1 = Binder.from(Block.Type.class, String.class, ThreadContext.class, Block.class).invokeStaticQuiet(MethodHandles.lookup(), CompiledIRBlockBody.class, "foldType");
private static Block.Type foldType(String name, ThreadContext context, Block block) {
return block.type;
}

private static final MethodHandle FOLD_METHOD2 = Binder.from(String.class, ThreadContext.class, Block.class, IRubyObject.class).invokeStaticQuiet(MethodHandles.lookup(), CompiledIRBlockBody.class, "foldMethod");
private static String foldMethod(ThreadContext context, Block block, IRubyObject arg) {
return block.getBinding().getMethod();
}

private static final MethodHandle FOLD_TYPE2 = Binder.from(Block.Type.class, String.class, ThreadContext.class, Block.class, IRubyObject.class).invokeStaticQuiet(MethodHandles.lookup(), CompiledIRBlockBody.class, "foldType");
private static Block.Type foldType(String name, ThreadContext context, Block block, IRubyObject arg) {
return block.type;
}

private static final MethodHandle SET_NORMAL = Binder.from(void.class, ThreadContext.class, Block.class).drop(1).append(Block.Type.NORMAL).invokeVirtualQuiet(MethodHandles.lookup(), "setCurrentBlockType");

private static final MethodHandle VALUE_TO_ARRAY = Binder.from(IRubyObject[].class, IRubyObject.class).invokeStaticQuiet(MethodHandles.lookup(), IRRuntimeHelpers.class, "singleBlockArgToArray");

private static final MethodHandle WRAP_VALUE = Binder.from(IRubyObject[].class, IRubyObject.class).invokeStaticQuiet(MethodHandles.lookup(), CompiledIRBlockBody.class, "wrapValue");

private static IRubyObject[] wrapValue(IRubyObject value) { return new IRubyObject[] {value}; }

@Override
public ArgumentDescriptor[] getArgumentDescriptors() {
return closure.getArgumentDescriptors();
@@ -33,6 +71,57 @@ public MethodHandle getHandle() {
return handle;
}

public MethodHandle getNormalYieldSpecificHandle() {
if (normalYieldSpecificHandle != null) return normalYieldSpecificHandle;

return normalYieldSpecificHandle = Binder.from(IRubyObject.class, ThreadContext.class, Block.class)
.foldVoid(SET_NORMAL)
.fold(FOLD_METHOD1)
.fold(FOLD_TYPE1)
.append(getStaticScope())
.append(IRubyObject.class, null)
.append(IRubyObject[].class, null)
.append(Block.class, Block.NULL_BLOCK)
.permute(2, 3, 4, 5, 6, 7, 1, 0)
.invoke(handle);
}

public MethodHandle getNormalYieldHandle() {
if (normalYieldHandle != null) return normalYieldHandle;

return normalYieldHandle = Binder.from(IRubyObject.class, ThreadContext.class, Block.class, IRubyObject.class)
.foldVoid(SET_NORMAL)
.fold(FOLD_METHOD2)
.fold(FOLD_TYPE2)
.filter(4, WRAP_VALUE)
.insert(4, getStaticScope())
.insert(5, IRubyObject.class, null)
.append(Block.class, Block.NULL_BLOCK)
.permute(2, 3, 4, 5, 6, 7, 1, 0)
.invoke(handle);
}

public MethodHandle getNormalYieldUnwrapHandle() {
if (normalYieldUnwrapHandle != null) return normalYieldUnwrapHandle;

return normalYieldUnwrapHandle = Binder.from(IRubyObject.class, ThreadContext.class, Block.class, IRubyObject.class)
.foldVoid(SET_NORMAL)
.fold(FOLD_METHOD2)
.fold(FOLD_TYPE2)
.filter(4, VALUE_TO_ARRAY)
.insert(4, getStaticScope())
.insert(5, IRubyObject.class, null)
.append(Block.class, Block.NULL_BLOCK)
.permute(2, 3, 4, 5, 6, 7, 1, 0)
.invoke(handle);
}

public MethodHandle getTestBlockBody() {
if (testBlockBody != null) return testBlockBody;

return testBlockBody = Binder.from(boolean.class, ThreadContext.class, Block.class).drop(0).append(this).invoke(TEST_BLOCK_BODY);
}

@Override
protected IRubyObject callDirect(ThreadContext context, Block block, IRubyObject[] args, Block blockArg) {
context.setCurrentBlockType(Block.Type.PROC);