Skip to content

Commit

Permalink
Showing 17 changed files with 195 additions and 93 deletions.
2 changes: 1 addition & 1 deletion core/pom.rb
Original file line number Diff line number Diff line change
@@ -57,7 +57,7 @@
jar 'org.jruby.jcodings:jcodings:1.0.18'
jar 'org.jruby:dirgra:0.3'

jar 'com.headius:invokebinder:1.7'
jar 'com.headius:invokebinder:1.8'
jar 'com.headius:options:1.4'
jar 'com.headius:unsafe-fences:1.0'

2 changes: 1 addition & 1 deletion core/pom.xml
Original file line number Diff line number Diff line change
@@ -193,7 +193,7 @@ DO NOT MODIFIY - GENERATED CODE
<dependency>
<groupId>com.headius</groupId>
<artifactId>invokebinder</artifactId>
<version>1.7</version>
<version>1.8-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.headius</groupId>
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/ir/IRBuilder.java
Original file line number Diff line number Diff line change
@@ -1321,7 +1321,7 @@ public int compare(Map.Entry<Integer, Label> o1, Map.Entry<Integer, Label> o2) {
expression = ((StringLiteral) expression).frozenString;
}

addInstr(new EQQInstr(eqqResult, expression, value, true));
addInstr(new EQQInstr(eqqResult, expression, value, false));
v1 = eqqResult;
v2 = manager.getTrue();
}
Original file line number Diff line number Diff line change
@@ -56,6 +56,8 @@ public Operand getClosureArg() {
return getOperand1();
}

public boolean hasLiteralClosure() { return getClosureArg() instanceof WrappedIRClosure; }

@Override
public void encode(IRWriterEncoder e) {
super.encode(e);
8 changes: 8 additions & 0 deletions core/src/main/java/org/jruby/ir/instructions/CallBase.java
Original file line number Diff line number Diff line change
@@ -425,6 +425,14 @@ public Object interpret(ThreadContext context, StaticScope currScope, DynamicSco
IRubyObject[] values = prepareArguments(context, self, currScope, dynamicScope, temp);
Block preparedBlock = prepareBlock(context, self, currScope, dynamicScope, temp);

if (hasLiteralClosure()) {
try {
return callSite.call(context, self, object, values, preparedBlock);
} finally {
preparedBlock.escape();
}
}

return callSite.call(context, self, object, values, preparedBlock);
}

Original file line number Diff line number Diff line change
@@ -8,4 +8,5 @@
*/
public interface ClosureAcceptingInstr {
public Operand getClosureArg();
public boolean hasLiteralClosure();
}
Original file line number Diff line number Diff line change
@@ -329,7 +329,7 @@ protected static void processCall(ThreadContext context, Instr instr, Operation
IRubyObject r = (IRubyObject)retrieveOp(call.getReceiver(), context, self, currDynScope, currScope, temp);
IRubyObject o = (IRubyObject)call.getArg1().retrieve(context, self, currScope, currDynScope, temp);
Block preparedBlock = call.prepareBlock(context, self, currScope, currDynScope, temp);
result = call.getCallSite().call(context, self, r, o, preparedBlock);
result = call.getCallSite().callIter(context, self, r, o, preparedBlock);
setResult(temp, currDynScope, call.getResult(), result);
break;
}
@@ -419,7 +419,6 @@ protected static void processBookKeepingOp(ThreadContext context, Block block, I
protected static IRubyObject processReturnOp(ThreadContext context, Block block, Instr instr, Operation operation,
DynamicScope currDynScope, Object[] temp, IRubyObject self,
StaticScope currScope) {
Block.Type blockType = block == null ? null : block.type;
switch(operation) {
// --------- Return flavored instructions --------
case RETURN: {
@@ -433,12 +432,12 @@ protected static IRubyObject processReturnOp(ThreadContext context, Block block,
// This assumes that scopes with break instr. have a frame / dynamic scope
// pushed so that we can get to its static scope. For-loops now always have
// a dyn-scope pushed onto stack which makes this work in all scenarios.
return IRRuntimeHelpers.initiateBreak(context, currDynScope, rv, blockType);
return IRRuntimeHelpers.initiateBreak(context, currDynScope, rv, block);
}
case NONLOCAL_RETURN: {
NonlocalReturnInstr ri = (NonlocalReturnInstr)instr;
IRubyObject rv = (IRubyObject)retrieveOp(ri.getReturnValue(), context, self, currDynScope, currScope, temp);
return IRRuntimeHelpers.initiateNonLocalReturn(context, currDynScope, blockType, rv);
return IRRuntimeHelpers.initiateNonLocalReturn(context, currDynScope, block, rv);
}
case RETURN_OR_RETHROW_SAVED_EXC: {
IRubyObject retVal = (IRubyObject) retrieveOp(((ReturnBase) instr).getReturnValue(), context, self, currDynScope, currScope, temp);
21 changes: 11 additions & 10 deletions core/src/main/java/org/jruby/ir/runtime/IRRuntimeHelpers.java
Original file line number Diff line number Diff line change
@@ -95,8 +95,8 @@ public static void checkForLJE(ThreadContext context, DynamicScope dynScope, boo
}

// Create a jump for a non-local return which will return from nearest lambda (which may be itself) or method.
public static IRubyObject initiateNonLocalReturn(ThreadContext context, DynamicScope dynScope, Block.Type blockType, IRubyObject returnValue) {
if (IRRuntimeHelpers.inLambda(blockType)) throw new IRWrappedLambdaReturnValue(returnValue);
public static IRubyObject initiateNonLocalReturn(ThreadContext context, DynamicScope dynScope, Block block, IRubyObject returnValue) {
if (block != null && IRRuntimeHelpers.inLambda(block.type)) throw new IRWrappedLambdaReturnValue(returnValue);

throw IRReturnJump.create(getContainingMethodOrLambdasDynamicScope(dynScope), returnValue);
}
@@ -159,15 +159,21 @@ private static IRScopeType ensureScopeIsClosure(ThreadContext context, DynamicSc
}

// FIXME: When we recompile lambdas we can eliminate this binary code path and we can emit as a NONLOCALRETURN directly.
public static IRubyObject initiateBreak(ThreadContext context, DynamicScope dynScope, IRubyObject breakValue, Block.Type blockType) throws RuntimeException {
public static IRubyObject initiateBreak(ThreadContext context, DynamicScope dynScope, IRubyObject breakValue, Block block) throws RuntimeException {
// Wrap the return value in an exception object and push it through the break exception
// paths so that ensures are run, frames/scopes are popped from runtime stacks, etc.
if (inLambda(blockType)) throw new IRWrappedLambdaReturnValue(breakValue);
if (inLambda(block.type)) throw new IRWrappedLambdaReturnValue(breakValue);

IRScopeType scopeType = ensureScopeIsClosure(context, dynScope);

DynamicScope parentScope = dynScope.getParentScope();

if (block.isEscaped()) {
throw context.runtime.newLocalJumpError(RubyLocalJumpError.Reason.BREAK, breakValue, "unexpected break");
}

// Raise a break jump so we can bubble back down the stack to the appropriate place to break from.
throw IRBreakJump.create(dynScope.getParentScope(), breakValue, scopeType.isEval()); // weirdly evals are impld as closures...yes yes.
throw IRBreakJump.create(parentScope, breakValue, scopeType.isEval()); // weirdly evals are impld as closures...yes yes.
}

// Are we within the scope where we want to return the value we are passing down the stack?
@@ -402,11 +408,6 @@ public static IRubyObject isEQQ(ThreadContext context, IRubyObject receiver, IRu
return isUndefValue ? receiver : callSite.call(context, receiver, receiver, value);
}

@Deprecated
public static IRubyObject isEQQ(ThreadContext context, IRubyObject receiver, IRubyObject value, CallSite callSite) {
return isEQQ(context, receiver, value, callSite, true);
}

public static IRubyObject newProc(Ruby runtime, Block block) {
return (block == Block.NULL_BLOCK) ? runtime.getNil() : runtime.newProc(Block.Type.PROC, block);
}
Original file line number Diff line number Diff line change
@@ -31,7 +31,7 @@
*/
public class ArrayDerefInvokeSite extends NormalInvokeSite {
public ArrayDerefInvokeSite(MethodType type, String file, int line) {
super(type, "[]", file, line);
super(type, "[]", false, file, line);
}

public static final Handle BOOTSTRAP = new Handle(Opcodes.H_INVOKESTATIC, p(ArrayDerefInvokeSite.class), "bootstrap", sig(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, String.class, int.class));
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/ir/targets/Bootstrap.java
Original file line number Diff line number Diff line change
@@ -485,7 +485,7 @@ static MethodHandle buildJittedHandle(InvokeSite site, DynamicMethod method, boo
// Temporary fix for missing kwargs dup+splitting logic from frobnicate, called by CompiledIRMethod but
// skipped by indy's direct binding.
if (compiledIRMethod.hasKwargs()) return null;

// attempt IR direct binding
// TODO: this will have to expand when we start specializing arities

41 changes: 37 additions & 4 deletions core/src/main/java/org/jruby/ir/targets/IRBytecodeAdapter.java
Original file line number Diff line number Diff line change
@@ -9,6 +9,8 @@
import org.jruby.Ruby;
import org.jruby.RubyClass;
import org.jruby.compiler.impl.SkinnyMethodAdapter;
import org.jruby.ir.instructions.ClosureAcceptingInstr;
import org.jruby.ir.operands.Operand;
import org.jruby.ir.operands.UndefinedValue;
import org.jruby.ir.runtime.IRRuntimeHelpers;
import org.jruby.runtime.CallType;
@@ -361,9 +363,9 @@ public org.objectweb.asm.Label newLabel() {
*
* @param name name of the method to invoke
* @param arity arity of the call
* @param hasClosure whether a closure will be on the stack for passing
* @param blockPassType what type of closure is passed
*/
public abstract void invokeOther(String file, int line, String name, int arity, boolean hasClosure, boolean isPotentiallyRefined);
public abstract void invokeOther(String file, int line, String name, int arity, BlockPassType blockPassType, boolean isPotentiallyRefined);

/**
* Invoke the array dereferencing method ([]) on an object other than self.
@@ -394,6 +396,30 @@ public org.objectweb.asm.Label newLabel() {
*/
public abstract void invokeOtherOneFloat(String file, int line, String name, double flote, CallType callType);

public enum BlockPassType {
NONE(false, false),
GIVEN(true, false),
LITERAL(true, true);

private final boolean given;
private final boolean literal;

BlockPassType(boolean given, boolean literal) {
this.given = given;
this.literal = literal;
}

public boolean given() {
return given;
}
public boolean literal() {
return literal;
}
public static BlockPassType fromIR(ClosureAcceptingInstr callInstr) {
Operand closure = callInstr.getClosureArg();
return closure != null ? ( callInstr.hasLiteralClosure() ? BlockPassType.LITERAL : BlockPassType.GIVEN) : BlockPassType.NONE;
}
}

/**
* Invoke a method on self.
@@ -404,10 +430,10 @@ public org.objectweb.asm.Label newLabel() {
* @param line the line number where this call appears
* @param name name of the method to invoke
* @param arity arity of the call
* @param hasClosure whether a closure will be on the stack for passing
* @param blockPassType what type of closure is passed
* @param callType
*/
public abstract void invokeSelf(String file, int line, String name, int arity, boolean hasClosure, CallType callType, boolean isPotentiallyRefined);
public abstract void invokeSelf(String file, int line, String name, int arity, BlockPassType blockPassType, CallType callType, boolean isPotentiallyRefined);

/**
* Invoke a superclass method from an instance context.
@@ -626,6 +652,13 @@ public org.objectweb.asm.Label newLabel() {
*/
public abstract void prepareBlock(Handle handle, org.jruby.runtime.Signature signature, String className);

/**
* Perform a === call appropriate for a case/when statement.
*
* Stack required: context, case value, when value
*/
public abstract void callEqq(boolean isSplattedValue);

public SkinnyMethodAdapter adapter;
private int variableCount = 0;
private Map<Integer, Type> variableTypes = new HashMap<Integer, Type>();
42 changes: 26 additions & 16 deletions core/src/main/java/org/jruby/ir/targets/IRBytecodeAdapter6.java
Original file line number Diff line number Diff line change
@@ -382,8 +382,9 @@ public void run() {
});
}

public void invokeOther(String file, int line, String name, int arity, boolean hasClosure, boolean isPotentiallyRefined) {
invoke(file, line, name, arity, hasClosure, CallType.NORMAL, isPotentiallyRefined);
@Override
public void invokeOther(String file, int line, String name, int arity, BlockPassType blockPassType, boolean isPotentiallyRefined) {
invoke(file, line, name, arity, blockPassType, CallType.NORMAL, isPotentiallyRefined);
}

public void invokeArrayDeref(String file, int line) {
@@ -410,14 +411,15 @@ public void invokeArrayDeref(String file, int line) {
adapter.invokestatic(getClassData().clsName, methodName, incomingSig);
}

public void invoke(String file, int lineNumber, String name, int arity, boolean hasClosure, CallType callType, boolean isPotentiallyRefined) {
public void invoke(String file, int lineNumber, String name, int arity, BlockPassType blockPassType, CallType callType, boolean isPotentiallyRefined) {
if (arity > MAX_ARGUMENTS) throw new NotCompilableException("call to `" + name + "' has more than " + MAX_ARGUMENTS + " arguments");

SkinnyMethodAdapter adapter2;
String incomingSig;
String outgoingSig;

if (hasClosure) {
boolean blockGiven = blockPassType.given();
if (blockGiven) {
switch (arity) {
case -1:
incomingSig = sig(JVM.OBJECT, params(ThreadContext.class, JVM.OBJECT, JVM.OBJECT, JVM.OBJECT_ARRAY, Block.class));
@@ -478,29 +480,29 @@ public void invoke(String file, int lineNumber, String name, int arity, boolean
case -1:
case 1:
adapter2.aload(3);
if (hasClosure) adapter2.aload(4);
if (blockGiven) adapter2.aload(4);
break;
case 0:
if (hasClosure) adapter2.aload(3);
if (blockGiven) adapter2.aload(3);
break;
case 2:
adapter2.aload(3);
adapter2.aload(4);
if (hasClosure) adapter2.aload(5);
if (blockGiven) adapter2.aload(5);
break;
case 3:
adapter2.aload(3);
adapter2.aload(4);
adapter2.aload(5);
if (hasClosure) adapter2.aload(6);
if (blockGiven) adapter2.aload(6);
break;
default:
buildArrayFromLocals(adapter2, 3, arity);
if (hasClosure) adapter2.aload(3 + arity);
if (blockGiven) adapter2.aload(3 + arity);
break;
}

adapter2.invokevirtual(p(CachingCallSite.class), "call", outgoingSig);
adapter2.invokevirtual(p(CachingCallSite.class), blockPassType.literal() ? "callIter" : "call", outgoingSig);
adapter2.areturn();
adapter2.end();

@@ -533,9 +535,9 @@ public void invokeOtherOneFixnum(String file, int line, String name, long fixnum
if (!MethodIndex.hasFastFixnumOps(name)) {
pushFixnum(fixnum);
if (callType == CallType.NORMAL) {
invokeOther(file, line, name, 1, false, false);
invokeOther(file, line, name, 1, BlockPassType.NONE, false);
} else {
invokeSelf(file, line, name, 1, false, callType, false);
invokeSelf(file, line, name, 1, BlockPassType.NONE, callType, false);
}
return;
}
@@ -589,9 +591,9 @@ public void invokeOtherOneFloat(String file, int line, String name, double flote
if (!MethodIndex.hasFastFloatOps(name)) {
pushFloat(flote);
if (callType == CallType.NORMAL) {
invokeOther(file, line, name, 1, false, false);
invokeOther(file, line, name, 1, BlockPassType.NONE, false);
} else {
invokeSelf(file, line, name, 1, false, callType, false);
invokeSelf(file, line, name, 1, BlockPassType.NONE, callType, false);
}
return;
}
@@ -641,10 +643,10 @@ public void invokeOtherOneFloat(String file, int line, String name, double flote
adapter.invokestatic(getClassData().clsName, methodName, incomingSig);
}

public void invokeSelf(String file, int line, String name, int arity, boolean hasClosure, CallType callType, boolean isPotentiallyRefined) {
public void invokeSelf(String file, int line, String name, int arity, BlockPassType blockPassType, CallType callType, boolean isPotentiallyRefined) {
if (arity > MAX_ARGUMENTS) throw new NotCompilableException("call to `" + name + "' has more than " + MAX_ARGUMENTS + " arguments");

invoke(file, line, name, arity, hasClosure, callType, isPotentiallyRefined);
invoke(file, line, name, arity, blockPassType, callType, isPotentiallyRefined);
}

public void invokeInstanceSuper(String file, int line, String name, int arity, boolean hasClosure, boolean[] splatmap) {
@@ -1015,5 +1017,13 @@ public void prepareBlock(Handle handle, org.jruby.runtime.Signature signature, S
invokeIRHelper("prepareBlock", sig(Block.class, ThreadContext.class, IRubyObject.class, DynamicScope.class, BlockBody.class));
}

@Override
public void callEqq(boolean isSplattedValue) {
String siteName = getUniqueSiteName("===");
IRBytecodeAdapter.cacheCallSite(adapter, getClassData().clsName, siteName, "===", CallType.FUNCTIONAL, false);
adapter.ldc(isSplattedValue);
invokeIRHelper("isEQQ", sig(IRubyObject.class, ThreadContext.class, IRubyObject.class, IRubyObject.class, CallSite.class, boolean.class));
}

private final Map<Object, String> cacheFieldNames = new HashMap<>();
}
36 changes: 18 additions & 18 deletions core/src/main/java/org/jruby/ir/targets/IRBytecodeAdapter7.java
Original file line number Diff line number Diff line change
@@ -125,24 +125,24 @@ public void pushEncoding(Encoding encoding) {
adapter.invokedynamic("encoding", sig(RubyEncoding.class, ThreadContext.class), Bootstrap.contextValueString(), new String(encoding.getName()));
}

public void invokeOther(String file, int line, String name, int arity, boolean hasClosure, boolean isPotentiallyRefined) {
public void invokeOther(String file, int line, String name, int arity, BlockPassType blockPassType, boolean isPotentiallyRefined) {
if (arity > MAX_ARGUMENTS) throw new NotCompilableException("call to `" + name + "' has more than " + MAX_ARGUMENTS + " arguments");
if (isPotentiallyRefined) {
super.invokeOther(file, line, name, arity, hasClosure, isPotentiallyRefined);
super.invokeOther(file, line, name, arity, blockPassType, isPotentiallyRefined);
return;
}

if (hasClosure) {
if (blockPassType.given()) {
if (arity == -1) {
adapter.invokedynamic("invoke:" + JavaNameMangler.mangleMethodName(name), sig(JVM.OBJECT, params(ThreadContext.class, JVM.OBJECT, JVM.OBJECT, JVM.OBJECT_ARRAY, Block.class)), NormalInvokeSite.BOOTSTRAP, file, line);
adapter.invokedynamic("invoke:" + JavaNameMangler.mangleMethodName(name), sig(JVM.OBJECT, params(ThreadContext.class, JVM.OBJECT, JVM.OBJECT, JVM.OBJECT_ARRAY, Block.class)), NormalInvokeSite.BOOTSTRAP, blockPassType.literal(), file, line);
} else {
adapter.invokedynamic("invoke:" + JavaNameMangler.mangleMethodName(name), sig(JVM.OBJECT, params(ThreadContext.class, JVM.OBJECT, arity + 2, Block.class)), NormalInvokeSite.BOOTSTRAP, file, line);
adapter.invokedynamic("invoke:" + JavaNameMangler.mangleMethodName(name), sig(JVM.OBJECT, params(ThreadContext.class, JVM.OBJECT, arity + 2, Block.class)), NormalInvokeSite.BOOTSTRAP, blockPassType.literal(), file, line);
}
} else {
if (arity == -1) {
adapter.invokedynamic("invoke:" + JavaNameMangler.mangleMethodName(name), sig(JVM.OBJECT, params(ThreadContext.class, JVM.OBJECT, JVM.OBJECT, JVM.OBJECT_ARRAY)), NormalInvokeSite.BOOTSTRAP, file, line);
adapter.invokedynamic("invoke:" + JavaNameMangler.mangleMethodName(name), sig(JVM.OBJECT, params(ThreadContext.class, JVM.OBJECT, JVM.OBJECT, JVM.OBJECT_ARRAY)), NormalInvokeSite.BOOTSTRAP, false, file, line);
} else {
adapter.invokedynamic("invoke:" + JavaNameMangler.mangleMethodName(name), sig(JVM.OBJECT, params(ThreadContext.class, JVM.OBJECT, JVM.OBJECT, JVM.OBJECT, arity)), NormalInvokeSite.BOOTSTRAP, file, line);
adapter.invokedynamic("invoke:" + JavaNameMangler.mangleMethodName(name), sig(JVM.OBJECT, params(ThreadContext.class, JVM.OBJECT, JVM.OBJECT, JVM.OBJECT, arity)), NormalInvokeSite.BOOTSTRAP, false, file, line);
}
}
}
@@ -156,9 +156,9 @@ public void invokeOtherOneFixnum(String file, int line, String name, long fixnum
if (!MethodIndex.hasFastFixnumOps(name)) {
pushFixnum(fixnum);
if (callType == CallType.NORMAL) {
invokeOther(file, line, name, 1, false, false);
invokeOther(file, line, name, 1, BlockPassType.NONE,false);
} else {
invokeSelf(file, line, name, 1, false, callType, false);
invokeSelf(file, line, name, 1, BlockPassType.NONE, callType, false);
}
return;
}
@@ -179,9 +179,9 @@ public void invokeOtherOneFloat(String file, int line, String name, double flote
if (!MethodIndex.hasFastFloatOps(name)) {
pushFloat(flote);
if (callType == CallType.NORMAL) {
invokeOther(file, line, name, 1, false, false);
invokeOther(file, line, name, 1, BlockPassType.NONE, false);
} else {
invokeSelf(file, line, name, 1, false, callType, false);
invokeSelf(file, line, name, 1, BlockPassType.NONE, callType, false);
}
return;
}
@@ -198,25 +198,25 @@ public void invokeOtherOneFloat(String file, int line, String name, double flote
0);
}

public void invokeSelf(String file, int line, String name, int arity, boolean hasClosure, CallType callType, boolean isPotentiallyRefined) {
public void invokeSelf(String file, int line, String name, int arity, BlockPassType blockPassType, CallType callType, boolean isPotentiallyRefined) {
if (arity > MAX_ARGUMENTS) throw new NotCompilableException("call to `" + name + "' has more than " + MAX_ARGUMENTS + " arguments");
if (isPotentiallyRefined) {
super.invokeSelf(file, line, name, arity, hasClosure, callType, isPotentiallyRefined);
super.invokeSelf(file, line, name, arity, blockPassType, callType, isPotentiallyRefined);
return;
}

String action = callType == CallType.FUNCTIONAL ? "callFunctional" : "callVariable";
if (hasClosure) {
if (blockPassType != BlockPassType.NONE) {
if (arity == -1) {
adapter.invokedynamic(action + ":" + JavaNameMangler.mangleMethodName(name), sig(JVM.OBJECT, params(ThreadContext.class, JVM.OBJECT, JVM.OBJECT, JVM.OBJECT_ARRAY, Block.class)), SelfInvokeSite.BOOTSTRAP, file, line);
adapter.invokedynamic(action + ":" + JavaNameMangler.mangleMethodName(name), sig(JVM.OBJECT, params(ThreadContext.class, JVM.OBJECT, JVM.OBJECT, JVM.OBJECT_ARRAY, Block.class)), SelfInvokeSite.BOOTSTRAP, blockPassType.literal(), file, line);
} else {
adapter.invokedynamic(action + ":" + JavaNameMangler.mangleMethodName(name), sig(JVM.OBJECT, params(ThreadContext.class, JVM.OBJECT, arity + 2, Block.class)), SelfInvokeSite.BOOTSTRAP, file, line);
adapter.invokedynamic(action + ":" + JavaNameMangler.mangleMethodName(name), sig(JVM.OBJECT, params(ThreadContext.class, JVM.OBJECT, arity + 2, Block.class)), SelfInvokeSite.BOOTSTRAP, blockPassType.literal(), file, line);
}
} else {
if (arity == -1) {
adapter.invokedynamic(action + ":" + JavaNameMangler.mangleMethodName(name), sig(JVM.OBJECT, params(ThreadContext.class, JVM.OBJECT, JVM.OBJECT, JVM.OBJECT_ARRAY)), SelfInvokeSite.BOOTSTRAP, file, line);
adapter.invokedynamic(action + ":" + JavaNameMangler.mangleMethodName(name), sig(JVM.OBJECT, params(ThreadContext.class, JVM.OBJECT, JVM.OBJECT, JVM.OBJECT_ARRAY)), SelfInvokeSite.BOOTSTRAP, false, file, line);
} else {
adapter.invokedynamic(action + ":" + JavaNameMangler.mangleMethodName(name), sig(JVM.OBJECT, params(ThreadContext.class, JVM.OBJECT, JVM.OBJECT, JVM.OBJECT, arity)), SelfInvokeSite.BOOTSTRAP, file, line);
adapter.invokedynamic(action + ":" + JavaNameMangler.mangleMethodName(name), sig(JVM.OBJECT, params(ThreadContext.class, JVM.OBJECT, JVM.OBJECT, JVM.OBJECT, arity)), SelfInvokeSite.BOOTSTRAP, false, file, line);
}
}
}
45 changes: 42 additions & 3 deletions core/src/main/java/org/jruby/ir/targets/InvokeSite.java
Original file line number Diff line number Diff line change
@@ -33,6 +33,9 @@
import java.lang.invoke.MethodType;
import java.lang.invoke.MutableCallSite;
import java.lang.invoke.SwitchPoint;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;

import static java.lang.invoke.MethodHandles.lookup;
@@ -53,6 +56,7 @@ public abstract class InvokeSite extends MutableCallSite {
protected final String file;
protected final int line;
private boolean boundOnce;
private boolean literalClosure;
CacheEntry cache = CacheEntry.NULL_CACHE;

private static final Logger LOG = LoggerFactory.getLogger(InvokeSite.class);
@@ -64,9 +68,14 @@ public String name() {
public final CallType callType;

public InvokeSite(MethodType type, String name, CallType callType, String file, int line) {
this(type, name, callType, false, file, line);
}

public InvokeSite(MethodType type, String name, CallType callType, boolean literalClosure, String file, int line) {
super(type);
this.methodName = name;
this.callType = callType;
this.literalClosure = literalClosure;
this.file = file;
this.line = line;

@@ -140,11 +149,41 @@ public IRubyObject invoke(ThreadContext context, IRubyObject caller, IRubyObject

MethodHandle mh = getHandle(self, selfClass, method);

if (literalClosure) {
mh = Binder.from(mh.type())
.tryFinally(getBlockEscape(signature))
.invoke(mh);
}

updateInvocationTarget(mh, self, selfClass, entry.method, switchPoint);

if (literalClosure) {
try {
return method.call(context, self, selfClass, methodName, args, block);
} finally {
block.escape();
}
}

return method.call(context, self, selfClass, methodName, args, block);
}

private static final MethodHandle ESCAPE_BLOCK = Binder.from(void.class, Block.class).invokeVirtualQuiet(lookup(), "escape");
private static final Map<Signature, MethodHandle> BLOCK_ESCAPES = Collections.synchronizedMap(new HashMap<Signature, MethodHandle>());

private static MethodHandle getBlockEscape(Signature signature) {
Signature voidSignature = signature.changeReturn(void.class);
MethodHandle escape = BLOCK_ESCAPES.get(voidSignature);
if (escape == null) {
escape = SmartBinder.from(voidSignature)
.permute("block")
.invoke(ESCAPE_BLOCK)
.handle();
BLOCK_ESCAPES.put(voidSignature, escape);
}
return escape;
}

/**
* Failover version uses a monomorphic cache and DynamicMethod.call, as in non-indy.
*/
@@ -279,7 +318,7 @@ public Binder prepareBinder(boolean varargs) {
MethodHandle getHandle(IRubyObject self, RubyClass dispatchClass, DynamicMethod method) throws Throwable {
boolean blockGiven = signature.lastArgType() == Block.class;

MethodHandle mh = buildNewInstanceHandle(method, self, blockGiven);
MethodHandle mh = buildNewInstanceHandle(method, self);
if (mh == null) mh = Bootstrap.buildNativeHandle(this, method, blockGiven);
if (mh == null) mh = Bootstrap.buildIndyHandle(this, method, method.getImplementationClass());
if (mh == null) mh = Bootstrap.buildJittedHandle(this, method, blockGiven);
@@ -291,14 +330,14 @@ MethodHandle getHandle(IRubyObject self, RubyClass dispatchClass, DynamicMethod
return mh;
}

MethodHandle buildNewInstanceHandle(DynamicMethod method, IRubyObject self, boolean blockGiven) {
MethodHandle buildNewInstanceHandle(DynamicMethod method, IRubyObject self) {
MethodHandle mh = null;

if (method == self.getRuntime().getBaseNewMethod()) {
RubyClass recvClass = (RubyClass) self;

// Bind a second site as a dynamic invoker to guard against changes in new object's type
CallSite initSite = SelfInvokeSite.bootstrap(lookup(), "callFunctional:initialize", type(), file, line);
CallSite initSite = SelfInvokeSite.bootstrap(lookup(), "callFunctional:initialize", type(), literalClosure ? 1 : 0, file, line);
MethodHandle initHandle = initSite.dynamicInvoker();

MethodHandle allocFilter = Binder.from(IRubyObject.class, IRubyObject.class)
51 changes: 27 additions & 24 deletions core/src/main/java/org/jruby/ir/targets/JVMVisitor.java
Original file line number Diff line number Diff line change
@@ -23,6 +23,7 @@
import org.jruby.ir.persistence.IRDumper;
import org.jruby.ir.representations.BasicBlock;
import org.jruby.ir.runtime.IRRuntimeHelpers;
import org.jruby.ir.targets.IRBytecodeAdapter.BlockPassType;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.*;
import org.jruby.runtime.builtin.IRubyObject;
@@ -571,7 +572,7 @@ public void AttrAssignInstr(AttrAssignInstr attrAssignInstr) {
attrAssignInstr.getReceiver(),
callArgs.length,
null,
false,
BlockPassType.NONE,
attrAssignInstr.getReceiver() instanceof Self ? CallType.FUNCTIONAL : CallType.NORMAL,
null,
attrAssignInstr.isPotentiallyRefined());
@@ -829,7 +830,7 @@ public void BacktickInstr(BacktickInstr instr) {
jvmAdapter().invokeinterface(p(IRubyObject.class), "setFrozen", sig(void.class, boolean.class));

// invoke the "`" method on self
jvmMethod().invokeSelf(file, lastLine, "`", 1, false, CallType.FUNCTIONAL, false);
jvmMethod().invokeSelf(file, lastLine, "`", 1, BlockPassType.NONE, CallType.FUNCTIONAL, false);
jvmStoreLocal(instr.getResult());
}

@@ -854,8 +855,8 @@ public void BreakInstr(BreakInstr breakInstr) {
jvmMethod().loadContext();
jvmLoadLocal(DYNAMIC_SCOPE);
visit(breakInstr.getReturnValue());
jvmMethod().loadBlockType();
jvmAdapter().invokestatic(p(IRRuntimeHelpers.class), "initiateBreak", sig(IRubyObject.class, ThreadContext.class, DynamicScope.class, IRubyObject.class, Block.Type.class));
jvmMethod().loadSelfBlock();
jvmAdapter().invokestatic(p(IRRuntimeHelpers.class), "initiateBreak", sig(IRubyObject.class, ThreadContext.class, DynamicScope.class, IRubyObject.class, Block.class));
jvmMethod().returnValue();

}
@@ -1066,14 +1067,14 @@ public void CallInstr(CallInstr callInstr) {
Operand receiver = callInstr.getReceiver();
int numArgs = args.length;
Operand closure = callInstr.getClosureArg(null);
boolean hasClosure = closure != null;
BlockPassType blockPassType = BlockPassType.fromIR(callInstr);
CallType callType = callInstr.getCallType();
Variable result = callInstr.getResult();

compileCallCommon(m, name, args, receiver, numArgs, closure, hasClosure, callType, result, callInstr.isPotentiallyRefined());
compileCallCommon(m, name, args, receiver, numArgs, closure, blockPassType, callType, result, callInstr.isPotentiallyRefined());
}

private void compileCallCommon(IRBytecodeAdapter m, String name, Operand[] args, Operand receiver, int numArgs, Operand closure, boolean hasClosure, CallType callType, Variable result, boolean isPotentiallyRefined) {
private void compileCallCommon(IRBytecodeAdapter m, String name, Operand[] args, Operand receiver, int numArgs, Operand closure, BlockPassType blockPassType, CallType callType, Variable result, boolean isPotentiallyRefined) {
m.loadContext();
m.loadSelf(); // caller
visit(receiver);
@@ -1091,21 +1092,21 @@ private void compileCallCommon(IRBytecodeAdapter m, String name, Operand[] args,
}
}

if (hasClosure) {
if (blockPassType.given()) {
m.loadContext();
visit(closure);
m.invokeIRHelper("getBlockFromObject", sig(Block.class, ThreadContext.class, Object.class));
}

switch (callType) {
case FUNCTIONAL:
m.invokeSelf(file, lastLine, name, arity, hasClosure, CallType.FUNCTIONAL, isPotentiallyRefined);
m.invokeSelf(file, lastLine, name, arity, blockPassType, CallType.FUNCTIONAL, isPotentiallyRefined);
break;
case VARIABLE:
m.invokeSelf(file, lastLine, name, arity, hasClosure, CallType.VARIABLE, isPotentiallyRefined);
m.invokeSelf(file, lastLine, name, arity, blockPassType, CallType.VARIABLE, isPotentiallyRefined);
break;
case NORMAL:
m.invokeOther(file, lastLine, name, arity, hasClosure, isPotentiallyRefined);
m.invokeOther(file, lastLine, name, arity, blockPassType, isPotentiallyRefined);
break;
}

@@ -1327,14 +1328,16 @@ public void DefineModuleInstr(DefineModuleInstr definemoduleinstr) {

@Override
public void EQQInstr(EQQInstr eqqinstr) {
jvmMethod().loadContext();
visit(eqqinstr.getArg1());
visit(eqqinstr.getArg2());
String siteName = jvmMethod().getUniqueSiteName("===");
IRBytecodeAdapter.cacheCallSite(jvmAdapter(), jvmMethod().getClassData().clsName, siteName, "===", CallType.FUNCTIONAL, false);
jvmAdapter().ldc(eqqinstr.isSplattedValue());
jvmMethod().invokeIRHelper("isEQQ", sig(IRubyObject.class, ThreadContext.class, IRubyObject.class, IRubyObject.class, CallSite.class, boolean.class));
jvmStoreLocal(eqqinstr.getResult());
if (!eqqinstr.isSplattedValue() && !(eqqinstr.getArg2() instanceof UndefinedValue)) {
// FIXME: eqq for case/when can be refined but we don't handle that
compileCallCommon(jvmMethod(), "===", Helpers.arrayOf(eqqinstr.getArg2()), eqqinstr.getArg1(), 1, null, BlockPassType.NONE, CallType.FUNCTIONAL, eqqinstr.getResult(), false);
} else {
jvmMethod().loadContext();
visit(eqqinstr.getArg1());
visit(eqqinstr.getArg2());
jvmMethod().callEqq(eqqinstr.isSplattedValue());
jvmStoreLocal(eqqinstr.getResult());
}
}

@Override
@@ -1510,7 +1513,7 @@ public void LoadFrameClosure(LoadFrameClosureInstr loadframeclosureinstr) {

@Override
public void MatchInstr(MatchInstr matchInstr) {
compileCallCommon(jvmMethod(), "=~", matchInstr.getCallArgs(), matchInstr.getReceiver(), 1, null, false, CallType.NORMAL, matchInstr.getResult(), false);
compileCallCommon(jvmMethod(), "=~", matchInstr.getCallArgs(), matchInstr.getReceiver(), 1, null, BlockPassType.NONE, CallType.NORMAL, matchInstr.getResult(), false);
}

@Override
@@ -1532,10 +1535,10 @@ public void NoResultCallInstr(NoResultCallInstr noResultCallInstr) {
Operand receiver = noResultCallInstr.getReceiver();
int numArgs = args.length;
Operand closure = noResultCallInstr.getClosureArg(null);
boolean hasClosure = closure != null;
BlockPassType blockPassType = BlockPassType.fromIR(noResultCallInstr);
CallType callType = noResultCallInstr.getCallType();

compileCallCommon(m, name, args, receiver, numArgs, closure, hasClosure, callType, null, noResultCallInstr.isPotentiallyRefined());
compileCallCommon(m, name, args, receiver, numArgs, closure, blockPassType, callType, null, noResultCallInstr.isPotentiallyRefined());
}

public void oneFixnumArgNoBlockCallInstr(OneFixnumArgNoBlockCallInstr oneFixnumArgNoBlockCallInstr) {
@@ -2061,10 +2064,10 @@ public void ToggleBacktraceInstr(ToggleBacktraceInstr instr) {
public void NonlocalReturnInstr(NonlocalReturnInstr returninstr) {
jvmMethod().loadContext();
jvmLoadLocal(DYNAMIC_SCOPE);
jvmMethod().loadBlockType();
jvmMethod().loadSelfBlock();
visit(returninstr.getReturnValue());

jvmMethod().invokeIRHelper("initiateNonLocalReturn", sig(IRubyObject.class, ThreadContext.class, DynamicScope.class, Block.Type.class, IRubyObject.class));
jvmMethod().invokeIRHelper("initiateNonLocalReturn", sig(IRubyObject.class, ThreadContext.class, DynamicScope.class, Block.class, IRubyObject.class));
jvmMethod().returnValue();
}

11 changes: 6 additions & 5 deletions core/src/main/java/org/jruby/ir/targets/NormalInvokeSite.java
Original file line number Diff line number Diff line change
@@ -24,15 +24,16 @@
public class NormalInvokeSite extends InvokeSite {
CacheEntry cache;

public NormalInvokeSite(MethodType type, String name, String file, int line) {
super(type, name, CallType.NORMAL, file, line);
public NormalInvokeSite(MethodType type, String name, boolean literalClosure, String file, int line) {
super(type, name, CallType.NORMAL, literalClosure, file, line);
}

public static Handle BOOTSTRAP = new Handle(Opcodes.H_INVOKESTATIC, p(NormalInvokeSite.class), "bootstrap", sig(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, String.class, int.class));
public static Handle BOOTSTRAP = new Handle(Opcodes.H_INVOKESTATIC, p(NormalInvokeSite.class), "bootstrap", sig(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, int.class, String.class, int.class));

public static CallSite bootstrap(MethodHandles.Lookup lookup, String name, MethodType type, String file, int line) {
public static CallSite bootstrap(MethodHandles.Lookup lookup, String name, MethodType type, int closureInt, String file, int line) {
boolean literalClosure = closureInt != 0;
String methodName = StringSupport.split(name, ':').get(1);
InvokeSite site = new NormalInvokeSite(type, JavaNameMangler.demangleMethodName(methodName), file, line);
InvokeSite site = new NormalInvokeSite(type, JavaNameMangler.demangleMethodName(methodName), literalClosure, file, line);

return InvokeSite.bootstrap(site, lookup);
}
13 changes: 9 additions & 4 deletions core/src/main/java/org/jruby/ir/targets/SelfInvokeSite.java
Original file line number Diff line number Diff line change
@@ -23,17 +23,22 @@
* Created by headius on 10/23/14.
*/
public class SelfInvokeSite extends InvokeSite {
public SelfInvokeSite(MethodType type, String name, CallType callType, boolean literalClosure, String file, int line) {
super(type, name, callType, literalClosure, file, line);
}

public SelfInvokeSite(MethodType type, String name, CallType callType, String file, int line) {
super(type, name, callType, file, line);
this(type, name, callType, false, file, line);
}

public static Handle BOOTSTRAP = new Handle(Opcodes.H_INVOKESTATIC, p(SelfInvokeSite.class), "bootstrap", sig(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, String.class, int.class));
public static Handle BOOTSTRAP = new Handle(Opcodes.H_INVOKESTATIC, p(SelfInvokeSite.class), "bootstrap", sig(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, int.class, String.class, int.class));

public static CallSite bootstrap(MethodHandles.Lookup lookup, String name, MethodType type, String file, int line) {
public static CallSite bootstrap(MethodHandles.Lookup lookup, String name, MethodType type, int closureInt, String file, int line) {
boolean literalClosure = closureInt != 0;
List<String> nameComponents = StringSupport.split(name, ':');
String methodName = JavaNameMangler.demangleMethodName(nameComponents.get(1));
CallType callType = nameComponents.get(0).equals("callFunctional") ? CallType.FUNCTIONAL : CallType.VARIABLE;
InvokeSite site = new SelfInvokeSite(type, methodName, callType, file, line);
InvokeSite site = new SelfInvokeSite(type, methodName, callType, literalClosure, file, line);

return InvokeSite.bootstrap(site, lookup);
}

0 comments on commit 210e637

Please sign in to comment.