Skip to content

Commit

Permalink
Showing 34 changed files with 319 additions and 251 deletions.
7 changes: 5 additions & 2 deletions core/src/main/java/org/jruby/ir/IRClosure.java
Original file line number Diff line number Diff line change
@@ -16,6 +16,7 @@
import org.jruby.runtime.BlockBody;
import org.jruby.runtime.IRBlockBody;
import org.jruby.runtime.MixedModeIRBlockBody;
import org.jruby.runtime.InterpretedIRBlockBody;
import org.jruby.runtime.Signature;
import org.objectweb.asm.Handle;

@@ -67,7 +68,8 @@ protected IRClosure(IRClosure c, IRScope lexicalParent, int closureId, String fu
if (getManager().isDryRun()) {
this.body = null;
} else {
this.body = new MixedModeIRBlockBody(c, c.getSignature());
boolean shouldJit = getManager().getInstanceConfig().getCompileMode().shouldJIT();
this.body = shouldJit ? new MixedModeIRBlockBody(c, c.getSignature()) : new InterpretedIRBlockBody(c, c.getSignature());
}

this.signature = c.signature;
@@ -90,7 +92,8 @@ public IRClosure(IRManager manager, IRScope lexicalParent, int lineNumber, Stati
if (getManager().isDryRun()) {
this.body = null;
} else {
this.body = new MixedModeIRBlockBody(this, signature);
boolean shouldJit = manager.getInstanceConfig().getCompileMode().shouldJIT();
this.body = shouldJit ? new MixedModeIRBlockBody(this, signature) : new InterpretedIRBlockBody(this, signature);
if (staticScope != null && !isBeginEndBlock) {
staticScope.setIRScope(this);
staticScope.setScopeType(this.getScopeType());
12 changes: 9 additions & 3 deletions core/src/main/java/org/jruby/ir/instructions/EQQInstr.java
Original file line number Diff line number Diff line change
@@ -9,16 +9,22 @@
import org.jruby.ir.runtime.IRRuntimeHelpers;
import org.jruby.ir.transformations.inlining.CloneInfo;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.CallSite;
import org.jruby.runtime.DynamicScope;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.callsite.FunctionalCachingCallSite;

// If v2 is an array, compare v1 with every element of v2 and stop on first match!
public class EQQInstr extends TwoOperandResultBaseInstr implements FixedArityInstr {
private final CallSite callSite;

public EQQInstr(Variable result, Operand v1, Operand v2) {
super(Operation.EQQ, result, v1, v2);

assert result != null: "EQQInstr result is null";

this.callSite = new FunctionalCachingCallSite("===");
}

public Operand getArg1() {
@@ -47,9 +53,9 @@ public static EQQInstr decode(IRReaderDecoder d) {

@Override
public Object interpret(ThreadContext context, StaticScope currScope, DynamicScope currDynScope, IRubyObject self, Object[] temp) {
return IRRuntimeHelpers.isEQQ(context,
(IRubyObject) getArg1().retrieve(context, self, currScope, currDynScope, temp),
(IRubyObject) getArg2().retrieve(context, self, currScope, currDynScope, temp));
IRubyObject recv = (IRubyObject) getArg1().retrieve(context, self, currScope, currDynScope, temp);
IRubyObject value = (IRubyObject) getArg2().retrieve(context, self, currScope, currDynScope, temp);
return IRRuntimeHelpers.isEQQ(context, recv, value, callSite);
}

@Override
26 changes: 14 additions & 12 deletions core/src/main/java/org/jruby/ir/interpreter/InterpreterEngine.java
Original file line number Diff line number Diff line change
@@ -106,16 +106,15 @@ public IRubyObject interpret(ThreadContext context, Block block, IRubyObject sel
return interpret(context, block, self, interpreterContext, implClass, name, new IRubyObject[] {arg1, arg2, arg3, arg4}, blockArg);
}

private DynamicScope getBlockScope(ThreadContext context, Block block, InterpreterContext interpreterContext) {
private DynamicScope getNewBlockScope(ThreadContext context, Block block, InterpreterContext interpreterContext) {
DynamicScope newScope = block.getBinding().getDynamicScope();
if (interpreterContext.pushNewDynScope()) {
context.pushScope(block.allocScope(newScope));
} else if (interpreterContext.reuseParentDynScope()) {
// Reuse! We can avoid the push only if surrounding vars aren't referenced!
context.pushScope(newScope);
}
if (interpreterContext.pushNewDynScope()) return block.allocScope(newScope);

// Reuse! We can avoid the push only if surrounding vars aren't referenced!
if (interpreterContext.reuseParentDynScope()) return newScope;

return newScope;
// No change
return null;
}

public IRubyObject interpret(ThreadContext context, Block block, IRubyObject self,
@@ -180,16 +179,19 @@ public IRubyObject interpret(ThreadContext context, Block block, IRubyObject sel
}
break;
case BOOK_KEEPING_OP:
// IMPORTANT: Preserve these update to currDynScope, self, and args.
// They affect execution of all following instructions in this scope.
switch (operation) {
case PUSH_METHOD_BINDING:
// IMPORTANT: Preserve this update of currDynScope.
// This affects execution of all instructions in this scope
// which will now use the updated value of currDynScope.
currDynScope = interpreterContext.newDynamicScope(context);
context.pushScope(currDynScope);
break;
case PUSH_BLOCK_BINDING:
currDynScope = getBlockScope(context, block, interpreterContext);
DynamicScope newScope = getNewBlockScope(context, block, interpreterContext);
if (newScope != null) {
currDynScope = newScope;
context.pushScope(currDynScope);
}
break;
case UPDATE_BLOCK_STATE:
if (self == null || block.getEvalType() == EvalType.BINDING_EVAL) {
184 changes: 113 additions & 71 deletions core/src/main/java/org/jruby/ir/passes/AddCallProtocolInstructions.java
Original file line number Diff line number Diff line change
@@ -3,9 +3,11 @@
import org.jruby.ir.*;
import org.jruby.ir.dataflow.analyses.StoreLocalVarPlacementProblem;
import org.jruby.ir.instructions.*;
import org.jruby.runtime.Signature;
import org.jruby.ir.operands.ImmutableLiteral;
import org.jruby.ir.operands.Label;
import org.jruby.ir.operands.Operand;
import org.jruby.ir.operands.Self;
import org.jruby.ir.operands.TemporaryVariable;
import org.jruby.ir.operands.Variable;
import org.jruby.ir.representations.BasicBlock;
@@ -20,7 +22,10 @@ public String getLabel() {
}

private boolean explicitCallProtocolSupported(IRScope scope) {
return scope instanceof IRMethod || (scope instanceof IRModuleBody && !(scope instanceof IRMetaClassBody));
return scope instanceof IRMethod
// SSS: Turning this off till this is fully debugged
// || (scope instanceof IRClosure && !(scope instanceof IREvalScript))
|| (scope instanceof IRModuleBody && !(scope instanceof IRMetaClassBody));
}

/*
@@ -41,6 +46,16 @@ private void fixReturn(IRScope scope, ReturnBase i, ListIterator<Instr> instrs)
}
}

private void popSavedState(IRScope scope, boolean requireBinding, boolean requireFrame, Variable savedViz, Variable savedFrame, ListIterator<Instr> instrs) {
if (requireBinding) instrs.add(new PopBindingInstr());
if (scope instanceof IRClosure) {
instrs.add(new PopBlockFrameInstr(savedFrame));
instrs.add(new RestoreBindingVisibilityInstr(savedViz));
} else {
if (requireFrame) instrs.add(new PopMethodFrameInstr());
}
}

@Override
public Object execute(IRScope scope, Object... data) {
// IRScriptBody do not get explicit call protocol instructions right now.
@@ -50,95 +65,122 @@ public Object execute(IRScope scope, Object... data) {
// Add explicit frame and binding push/pop instrs ONLY for methods -- we cannot handle this in closures and evals yet
// If the scope uses $_ or $~ family of vars, has local load/stores, or if its binding has escaped, we have
// to allocate a dynamic scope for it and add binding push/pop instructions.
if (explicitCallProtocolSupported(scope)) {
StoreLocalVarPlacementProblem slvpp = scope.getStoreLocalVarPlacementProblem();
boolean scopeHasLocalVarStores = false;
boolean bindingHasEscaped = scope.bindingHasEscaped();
if (!explicitCallProtocolSupported(scope)) return null;

CFG cfg = scope.getCFG();
StoreLocalVarPlacementProblem slvpp = scope.getStoreLocalVarPlacementProblem();
boolean scopeHasLocalVarStores = false;
boolean bindingHasEscaped = scope.bindingHasEscaped();

if (slvpp != null && bindingHasEscaped) {
scopeHasLocalVarStores = slvpp.scopeHasLocalVarStores();
} else {
// We dont require local-var load/stores to have been run.
// If it is not run, we go conservative and add push/pop binding instrs. everywhere
scopeHasLocalVarStores = bindingHasEscaped;
}
CFG cfg = scope.getCFG();

boolean requireFrame = doesItRequireFrame(scope, bindingHasEscaped);
boolean requireBinding = !scope.getFlags().contains(IRFlags.DYNSCOPE_ELIMINATED);
if (slvpp != null && bindingHasEscaped) {
scopeHasLocalVarStores = slvpp.scopeHasLocalVarStores();
} else {
// We dont require local-var load/stores to have been run.
// If it is not run, we go conservative and add push/pop binding instrs. everywhere
scopeHasLocalVarStores = bindingHasEscaped;
}

if (requireBinding || requireFrame) {
BasicBlock entryBB = cfg.getEntryBB();
// Push
// For now, we always require frame for closures
boolean requireFrame = doesItRequireFrame(scope, bindingHasEscaped);
boolean reuseParentDynScope = scope.getFlags().contains(IRFlags.REUSE_PARENT_DYNSCOPE);
boolean requireBinding = reuseParentDynScope || !scope.getFlags().contains(IRFlags.DYNSCOPE_ELIMINATED);

if (scope instanceof IRClosure || requireBinding || requireFrame) {
BasicBlock entryBB = cfg.getEntryBB();
Variable savedViz = null, savedFrame = null;
if (scope instanceof IRClosure) {
savedViz = scope.createTemporaryVariable();
savedFrame = scope.createTemporaryVariable();
entryBB.addInstr(new SaveBindingVisibilityInstr(savedViz));
entryBB.addInstr(new PushBlockFrameInstr(savedFrame, scope.getName()));
if (requireBinding) entryBB.addInstr(new PushBlockBindingInstr());
entryBB.addInstr(new UpdateBlockExecutionStateInstr(Self.SELF));
Signature sig = ((IRClosure)scope).getSignature();

// If it doesn't need any args, no arg preparation involved!
int arityValue = sig.arityValue();
if (arityValue != 0) {
// Add the right kind of arg preparation instruction
if (sig.isFixed()) {
if (arityValue == 1) {
entryBB.addInstr(new PrepareSingleBlockArgInstr());
} else {
entryBB.addInstr(new PrepareFixedBlockArgsInstr());
}
} else {
entryBB.addInstr(new PrepareBlockArgsInstr(Operation.PREPARE_BLOCK_ARGS));
}
}
} else {
if (requireFrame) entryBB.addInstr(new PushMethodFrameInstr(scope.getName()));
if (requireBinding) entryBB.addInstr(new PushMethodBindingInstr());
}

// SSS FIXME: We are doing this conservatively.
// Only scopes that have unrescued exceptions need a GEB.
//
// Allocate GEB if necessary for popping
BasicBlock geb = cfg.getGlobalEnsureBB();
if (geb == null) {
Variable exc = scope.createTemporaryVariable();
geb = new BasicBlock(cfg, Label.getGlobalEnsureBlockLabel());
geb.addInstr(new ReceiveJRubyExceptionInstr(exc)); // JRuby Implementation exception handling
geb.addInstr(new ThrowExceptionInstr(exc));
cfg.addGlobalEnsureBB(geb);
}
// SSS FIXME: We are doing this conservatively.
// Only scopes that have unrescued exceptions need a GEB.
//
// Allocate GEB if necessary for popping
BasicBlock geb = cfg.getGlobalEnsureBB();
boolean gebProcessed = false;
if (geb == null) {
Variable exc = scope.createTemporaryVariable();
geb = new BasicBlock(cfg, Label.getGlobalEnsureBlockLabel());
geb.addInstr(new ReceiveJRubyExceptionInstr(exc)); // JRuby Implementation exception handling
geb.addInstr(new ThrowExceptionInstr(exc));
cfg.addGlobalEnsureBB(geb);
}

// Pop on all scope-exit paths
for (BasicBlock bb: cfg.getBasicBlocks()) {
Instr i = null;
ListIterator<Instr> instrs = bb.getInstrs().listIterator();
while (instrs.hasNext()) {
i = instrs.next();
// Right now, we only support explicit call protocol on methods.
// So, non-local returns and breaks don't get here.
// Non-local-returns and breaks are tricky since they almost always
// throw an exception and we don't multiple pops (once before the
// return/break, and once when the exception is caught).
if (!bb.isExitBB() && i instanceof ReturnBase) {
if (requireBinding || requireFrame) {
fixReturn(scope, (ReturnBase)i, instrs);
}
// Add before the break/return
instrs.previous();
if (requireBinding) instrs.add(new PopBindingInstr());
if (requireFrame) instrs.add(new PopMethodFrameInstr());
break;
// Pop on all scope-exit paths
for (BasicBlock bb: cfg.getBasicBlocks()) {
Instr i = null;
ListIterator<Instr> instrs = bb.getInstrs().listIterator();
while (instrs.hasNext()) {
i = instrs.next();
// Right now, we only support explicit call protocol on methods.
// So, non-local returns and breaks don't get here.
// Non-local-returns and breaks are tricky since they almost always
// throw an exception and we don't multiple pops (once before the
// return/break, and once when the exception is caught).
if (!bb.isExitBB() && i instanceof ReturnBase) {
if (requireBinding || requireFrame) {
fixReturn(scope, (ReturnBase)i, instrs);
}
// Add before the break/return
instrs.previous();
popSavedState(scope, requireBinding, requireFrame, savedViz, savedFrame, instrs);
if (bb == geb) gebProcessed = true;
break;
}
}

if (bb.isExitBB() && !bb.isEmpty()) {
// Last instr could be a return -- so, move iterator one position back
if (i != null && i instanceof ReturnBase) {
if (requireBinding || requireFrame) {
fixReturn(scope, (ReturnBase)i, instrs);
}
instrs.previous();
if (bb.isExitBB() && !bb.isEmpty()) {
// Last instr could be a return -- so, move iterator one position back
if (i != null && i instanceof ReturnBase) {
if (requireBinding || requireFrame) {
fixReturn(scope, (ReturnBase)i, instrs);
}
if (requireBinding) instrs.add(new PopBindingInstr());
if (requireFrame) instrs.add(new PopMethodFrameInstr());
instrs.previous();
}
popSavedState(scope, requireBinding, requireFrame, savedViz, savedFrame, instrs);
if (bb == geb) gebProcessed = true;
}

if (bb == geb) {
// Add before throw-exception-instr which would be the last instr
if (i != null) {
// Assumption: Last instr should always be a control-transfer instruction
assert i.getOperation().transfersControl(): "Last instruction of GEB in scope: " + scope + " is " + i + ", not a control-xfer instruction";
instrs.previous();
}
if (requireBinding) instrs.add(new PopBindingInstr());
if (requireFrame) instrs.add(new PopMethodFrameInstr());
if (!gebProcessed && bb == geb) {
// Add before throw-exception-instr which would be the last instr
if (i != null) {
// Assumption: Last instr should always be a control-transfer instruction
assert i.getOperation().transfersControl(): "Last instruction of GEB in scope: " + scope + " is " + i + ", not a control-xfer instruction";
instrs.previous();
}
popSavedState(scope, requireBinding, requireFrame, savedViz, savedFrame, instrs);
}
}

// This scope has an explicit call protocol flag now
scope.setExplicitCallProtocolFlag();
}

// This scope has an explicit call protocol flag now
scope.setExplicitCallProtocolFlag();

// LVA information is no longer valid after the pass
// FIXME: Grrr ... this seems broken to have to create a new object to invalidate
(new LiveVariableAnalysis()).invalidate(scope);
6 changes: 3 additions & 3 deletions core/src/main/java/org/jruby/ir/runtime/IRRuntimeHelpers.java
Original file line number Diff line number Diff line change
@@ -424,18 +424,18 @@ public static IRubyObject isExceptionHandled(ThreadContext context, IRubyObject
return context.runtime.newBoolean(ret);
}

public static IRubyObject isEQQ(ThreadContext context, IRubyObject receiver, IRubyObject value) {
public static IRubyObject isEQQ(ThreadContext context, IRubyObject receiver, IRubyObject value, CallSite callSite) {
boolean isUndefValue = value == UndefinedValue.UNDEFINED;
if (receiver instanceof RubyArray) {
RubyArray testVals = (RubyArray)receiver;
for (int i = 0, n = testVals.getLength(); i < n; i++) {
IRubyObject v = testVals.eltInternal(i);
IRubyObject eqqVal = isUndefValue ? v : v.callMethod(context, "===", value);
IRubyObject eqqVal = isUndefValue ? v : callSite.call(context, v, v, value);
if (eqqVal.isTrue()) return eqqVal;
}
return context.runtime.newBoolean(false);
} else {
return isUndefValue ? receiver : receiver.callMethod(context, "===", value);
return isUndefValue ? receiver : callSite.call(context, receiver, receiver, value);
}
}

60 changes: 60 additions & 0 deletions core/src/main/java/org/jruby/ir/targets/IRBytecodeAdapter.java
Original file line number Diff line number Diff line change
@@ -17,7 +17,13 @@
import org.jruby.runtime.Helpers;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.callsite.CachingCallSite;
import org.jruby.runtime.callsite.FunctionalCachingCallSite;
import org.jruby.runtime.callsite.NormalCachingCallSite;
import org.jruby.runtime.callsite.RefinedCachingCallSite;
import org.jruby.runtime.callsite.VariableCachingCallSite;
import org.jruby.util.ByteList;
import org.jruby.util.JavaNameMangler;
import org.jruby.util.RegexpOptions;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
@@ -44,6 +50,60 @@ public IRBytecodeAdapter(SkinnyMethodAdapter adapter, Signature signature, Class
this.classData = classData;
}

/**
* Utility to lazily construct and cache a call site object.
*
* @param method the SkinnyMethodAdapter to that's generating the containing method body
* @param className the name of the class in which the field will reside
* @param siteName the unique name of the site, used for the field
* @param rubyName the Ruby method name being invoked
* @param callType the type of call
* @param isPotentiallyRefined whether the call might be refined
*/
public static void cacheCallSite(SkinnyMethodAdapter method, String className, String siteName, String rubyName, CallType callType, boolean isPotentiallyRefined) {
// call site object field
method.getClassVisitor().visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC, siteName, ci(CachingCallSite.class), null, null).visitEnd();

// lazily construct it
method.getstatic(className, siteName, ci(CachingCallSite.class));
method.dup();
Label doCall = new Label();
method.ifnonnull(doCall);
method.pop();
method.ldc(rubyName);
Class<? extends CachingCallSite> siteClass;
String signature;
if (isPotentiallyRefined) {
siteClass = RefinedCachingCallSite.class;
signature = sig(siteClass, String.class, String.class);
method.ldc(callType.name());
} else {
switch (callType) {
case NORMAL:
siteClass = NormalCachingCallSite.class;
break;
case FUNCTIONAL:
siteClass = FunctionalCachingCallSite.class;
break;
case VARIABLE:
siteClass = VariableCachingCallSite.class;
break;
default:
throw new RuntimeException("BUG: Unexpected call type " + callType + " in JVM6 invoke logic");
}
signature = sig(siteClass, String.class);
}
method.invokestatic(p(IRRuntimeHelpers.class), "new" + siteClass.getSimpleName(), signature);
method.dup();
method.putstatic(className, siteName, ci(CachingCallSite.class));

method.label(doCall);
}

public String getUniqueSiteName(String name) {
return "invokeOther" + getClassData().callSiteCount.getAndIncrement() + ":" + JavaNameMangler.mangleMethodName(name);
}

public ClassData getClassData() {
return classData;
}
44 changes: 2 additions & 42 deletions core/src/main/java/org/jruby/ir/targets/IRBytecodeAdapter6.java
Original file line number Diff line number Diff line change
@@ -12,7 +12,6 @@
import org.jcodings.Encoding;
import org.jruby.Ruby;
import org.jruby.RubyArray;
import org.jruby.RubyBasicObject;
import org.jruby.RubyBignum;
import org.jruby.RubyBoolean;
import org.jruby.RubyClass;
@@ -36,10 +35,6 @@
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.callsite.CachingCallSite;
import org.jruby.runtime.callsite.FunctionalCachingCallSite;
import org.jruby.runtime.callsite.NormalCachingCallSite;
import org.jruby.runtime.callsite.RefinedCachingCallSite;
import org.jruby.runtime.callsite.VariableCachingCallSite;
import org.jruby.runtime.ivars.VariableAccessor;
import org.jruby.util.ByteList;
import org.jruby.util.JavaNameMangler;
@@ -331,7 +326,7 @@ public void invoke(String name, int arity, boolean hasClosure, CallType callType
}
}

String methodName = "invokeOther" + getClassData().callSiteCount.getAndIncrement() + ":" + JavaNameMangler.mangleMethodName(name);
String methodName = getUniqueSiteName(name);

adapter2 = new SkinnyMethodAdapter(
adapter.getClassVisitor(),
@@ -341,44 +336,9 @@ public void invoke(String name, int arity, boolean hasClosure, CallType callType
null,
null);

// call site object field
adapter.getClassVisitor().visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC, methodName, ci(CachingCallSite.class), null, null).visitEnd();

// lazily construct it
adapter2.getstatic(getClassData().clsName, methodName, ci(CachingCallSite.class));
adapter2.dup();
Label doCall = new Label();
adapter2.ifnonnull(doCall);
adapter2.pop();
adapter2.ldc(name);
Class<? extends CachingCallSite> siteClass;
String signature;
if (isPotentiallyRefined) {
siteClass = RefinedCachingCallSite.class;
signature = sig(siteClass, String.class, String.class);
adapter2.ldc(callType.name());
} else {
switch (callType) {
case NORMAL:
siteClass = NormalCachingCallSite.class;
break;
case FUNCTIONAL:
siteClass = FunctionalCachingCallSite.class;
break;
case VARIABLE:
siteClass = VariableCachingCallSite.class;
break;
default:
throw new RuntimeException("BUG: Unexpected call type " + callType + " in JVM6 invoke logic");
}
signature = sig(siteClass, String.class);
}
adapter2.invokestatic(p(IRRuntimeHelpers.class), "new" + siteClass.getSimpleName(), signature);
adapter2.dup();
adapter2.putstatic(getClassData().clsName, methodName, ci(CachingCallSite.class));
cacheCallSite(adapter2, getClassData().clsName, methodName, name, callType, isPotentiallyRefined);

// use call site to invoke
adapter2.label(doCall);
adapter2.aload(0); // context
adapter2.aload(1); // caller
adapter2.aload(2); // self
4 changes: 3 additions & 1 deletion core/src/main/java/org/jruby/ir/targets/JVMVisitor.java
Original file line number Diff line number Diff line change
@@ -1065,7 +1065,9 @@ public void EQQInstr(EQQInstr eqqinstr) {
jvmMethod().loadContext();
visit(eqqinstr.getArg1());
visit(eqqinstr.getArg2());
jvmMethod().invokeIRHelper("isEQQ", sig(IRubyObject.class, ThreadContext.class, IRubyObject.class, IRubyObject.class));
String siteName = jvmMethod().getUniqueSiteName("===");
IRBytecodeAdapter.cacheCallSite(jvmAdapter(), jvmMethod().getClassData().clsName, siteName, "===", CallType.FUNCTIONAL, false);
jvmMethod().invokeIRHelper("isEQQ", sig(IRubyObject.class, ThreadContext.class, IRubyObject.class, IRubyObject.class, CallSite.class));
jvmStoreLocal(eqqinstr.getResult());
}

10 changes: 10 additions & 0 deletions core/src/main/java/org/jruby/runtime/CompiledIRBlockBody.java
Original file line number Diff line number Diff line change
@@ -31,6 +31,16 @@ public ArgumentDescriptor[] getArgumentDescriptors() {
return closure.getArgumentDescriptors();
}

@Override
protected IRubyObject callDirect(ThreadContext context, Block block, IRubyObject[] args, Block blockArg) {
throw new RuntimeException("callDirect not implemented in CompiledIRBlockBody. Implement me!");
}

@Override
protected IRubyObject yieldDirect(ThreadContext context, Block block, IRubyObject[] args, IRubyObject self) {
throw new RuntimeException("yieldDirect not implemented in CompiledIRBlockBody. Implement me!");
}

protected IRubyObject commonYieldPath(ThreadContext context, Block block, IRubyObject[] args, IRubyObject self, Block blockArg) {
Binding binding = block.getBinding();
Visibility oldVis = binding.getFrame().getVisibility();
Original file line number Diff line number Diff line change
@@ -42,6 +42,7 @@ public void setCallCount(int callCount) {
@Override
public void completeBuild(InterpreterContext interpreterContext) {
this.interpreterContext = interpreterContext;
hasCallProtocolIR = closure.getFlags().contains(IRFlags.HAS_EXPLICIT_CALL_PROTOCOL);
}

@Override
@@ -63,8 +64,8 @@ public InterpreterContext ensureInstrsReady() {

if (interpreterContext == null) {
interpreterContext = closure.getInterpreterContext();
hasCallProtocolIR = closure.getFlags().contains(IRFlags.HAS_EXPLICIT_CALL_PROTOCOL);
}
hasCallProtocolIR = closure.getFlags().contains(IRFlags.HAS_EXPLICIT_CALL_PROTOCOL);
return interpreterContext;
}

27 changes: 27 additions & 0 deletions core/src/main/java/org/jruby/runtime/MixedModeIRBlockBody.java
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@
import org.jruby.RubyModule;
import org.jruby.compiler.Compilable;
import org.jruby.ir.IRClosure;
import org.jruby.ir.IRFlags;
import org.jruby.ir.IRScope;
import org.jruby.ir.interpreter.Interpreter;
import org.jruby.ir.interpreter.InterpreterContext;
@@ -50,6 +51,7 @@ public void completeBuild(CompiledIRBlockBody blockBody) {
this.callCount = -1;
blockBody.evalType = this.evalType; // share with parent
this.jittedBody = blockBody;
hasCallProtocolIR = closure.getFlags().contains(IRFlags.HAS_EXPLICIT_CALL_PROTOCOL);
}

@Override
@@ -75,6 +77,7 @@ public InterpreterContext ensureInstrsReady() {

if (interpreterContext == null) {
interpreterContext = closure.getInterpreterContext();
hasCallProtocolIR = closure.getFlags().contains(IRFlags.HAS_EXPLICIT_CALL_PROTOCOL);
}
return interpreterContext;
}
@@ -89,6 +92,30 @@ public String getName() {
return closure.getName();
}

@Override
protected IRubyObject callDirect(ThreadContext context, Block block, IRubyObject[] args, Block blockArg) {
if (callCount >= 0) promoteToFullBuild(context);
CompiledIRBlockBody jittedBody = this.jittedBody;
if (jittedBody != null) {
return jittedBody.callDirect(context, block, args, blockArg);
}

context.setCurrentBlockType(Block.Type.PROC);
return Interpreter.INTERPRET_BLOCK(context, block, null, interpreterContext, args, block.getBinding().getMethod(), blockArg);
}

@Override
protected IRubyObject yieldDirect(ThreadContext context, Block block, IRubyObject[] args, IRubyObject self) {
if (callCount >= 0) promoteToFullBuild(context);
CompiledIRBlockBody jittedBody = this.jittedBody;
if (jittedBody != null) {
return jittedBody.yieldDirect(context, block, args, self);
}

context.setCurrentBlockType(Block.Type.NORMAL);
return Interpreter.INTERPRET_BLOCK(context, block, self, interpreterContext, args, block.getBinding().getMethod(), Block.NULL_BLOCK);
}

protected IRubyObject commonYieldPath(ThreadContext context, Block block, IRubyObject[] args, IRubyObject self, Block blockArg) {
if (callCount >= 0) promoteToFullBuild(context);

2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/runtime/Signature.java
Original file line number Diff line number Diff line change
@@ -74,7 +74,7 @@ public boolean restKwargs() {
public boolean hasRest() { return rest != Rest.NONE; }

/**
* Are their an exact (fixed) number of parameters to this signature?
* Are there an exact (fixed) number of parameters to this signature?
*/
public boolean isFixed() {
return arityValue() >= 0;
3 changes: 0 additions & 3 deletions core/src/main/java/org/jruby/util/Memo.java
Original file line number Diff line number Diff line change
@@ -9,9 +9,6 @@
*/
package org.jruby.util;

/**
* @deprecated no longer used, should get removed
*/
public class Memo<T> {

private T value;
2 changes: 1 addition & 1 deletion spec/ruby/fixtures/code/concurrent.rb
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@
Thread.current[:in_concurrent_rb] = true

if t = Thread.current[:wait_for]
Thread.pass until t.backtrace.any? { |call| call.include? 'require' }
Thread.pass until t.backtrace && t.backtrace.any? { |call| call.include? 'require' }
end

if Thread.current[:con_raise]
13 changes: 10 additions & 3 deletions spec/truffle/specs/truffle/binding_of_caller_spec.rb
Original file line number Diff line number Diff line change
@@ -9,19 +9,26 @@
require_relative '../../../ruby/spec_helper'

describe "Truffle.binding_of_caller" do

def binding_of_caller
Truffle.binding_of_caller
end

#it "returns nil if there is no caller"
#end

it "returns a Binding" do
Truffle.binding_of_caller.should be_kind_of(Binding)
binding_of_caller.should be_kind_of(Binding)
end

it "gives read access to local variables at the call site" do
x = 14
Truffle.binding_of_caller.local_variable_get(:x).should == 14
binding_of_caller.local_variable_get(:x).should == 14
end

it "gives write access to local variables at the call site" do
x = 2
Truffle.binding_of_caller.local_variable_set(:x, 14)
binding_of_caller.local_variable_set(:x, 14)
x.should == 14
end

2 changes: 1 addition & 1 deletion spec/truffle/specs/truffle/cext/load_extconf_spec.rb
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@

it "raises a RuntimeError" do
lambda {
Truffle::CExt.load_extconf File.expand_path('fixtures/foo/ext/foo/extconf.rb', __FILE__)
Truffle::CExt.load_extconf File.expand_path('fixtures/foo/ext/foo/extconf.rb', File.dirname(__FILE__))
}.should raise_error(RuntimeError)
end

4 changes: 2 additions & 2 deletions spec/truffle/specs/truffle/primitive/coverage_result_spec.rb
Original file line number Diff line number Diff line change
@@ -10,8 +10,8 @@

describe "Truffle::Primitive.coverage_result" do

it "returns nil" do
Truffle::Primitive.coverage_result.should be_nil
it "returns an empty hash" do
Truffle::Primitive.coverage_result.should == {}
end

it "needs to be reviewed for spec completeness"
11 changes: 9 additions & 2 deletions spec/truffle/specs/truffle/source_of_caller_spec.rb
Original file line number Diff line number Diff line change
@@ -9,13 +9,20 @@
require_relative '../../../ruby/spec_helper'

describe "Truffle.source_of_caller" do

def source_of_caller
Truffle.source_of_caller
end

#it "returns nil if there is no caller"
#end

it "returns a String" do
Truffle.source_of_caller.should be_kind_of(String)
source_of_caller.should be_kind_of(String)
end

it "returns the name of the file at the call site" do
Truffle.source_of_caller.should == __FILE__
source_of_caller.should == __FILE__
end

it "works through #send" do
2 changes: 0 additions & 2 deletions spec/truffle/tags/truffle/binding_of_caller_tags.txt

This file was deleted.

1 change: 0 additions & 1 deletion spec/truffle/tags/truffle/cext/load_extconf_tags.txt

This file was deleted.

This file was deleted.

This file was deleted.

2 changes: 0 additions & 2 deletions spec/truffle/tags/truffle/source_of_caller_tags.txt

This file was deleted.

2 changes: 1 addition & 1 deletion spec/truffle/truffle.mspec
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ class MSpecScript
set :target, File.expand_path("../../../bin/jruby#{windows? ? '.bat' : ''}", __FILE__)

if ARGV[-2..-1] != %w[-t ruby] # No flags for MRI
set :flags, %w[-X+T -J-ea -J-esa -J-Xmx2G]
set :flags, %w[-X+T -J-ea -J-esa -J-Xmx2G -Xtruffle.coverage=true]
end

set :language, [
34 changes: 23 additions & 11 deletions tool/jt.rb
Original file line number Diff line number Diff line change
@@ -181,8 +181,11 @@ module Commands
include ShellUtils

def help
puts 'jt build build'
puts 'jt build truffle build only the Truffle part, assumes the rest is up-to-date'
puts 'jt checkout name checkout a different Git branch and rebuild'
puts 'jt build [options] build'
puts 'jt build truffle [options] build only the Truffle part, assumes the rest is up-to-date'
puts 'jt rebuild [options] clean and build'
puts ' --no-tests don\'t run JUnit unit tests'
puts 'jt clean clean'
puts 'jt irb irb'
puts 'jt rebuild clean and build'
@@ -227,15 +230,24 @@ def help
puts ' branch names are mangled - eg truffle-head becomes GRAAL_BIN_TRUFFLE_HEAD'
end

def build(project = nil)
opts = %w[-DskipTests]
case project
when 'truffle'
mvn *opts, '-pl', 'truffle', 'package'
when nil
mvn *opts, 'package'
else
raise ArgumentError, project
def checkout(branch)
sh 'git', 'checkout', branch
rebuild
end

def build(*args)
mvn_args = []

if args.delete 'truffle'
mvn_args += ['-pl', 'truffle', 'package']
end

if args.delete '--no-tests'
mvn_args << '-DskipTests'
end

unless args.empty?
raise ArgumentError, args.inspect
end
end

Original file line number Diff line number Diff line change
@@ -407,7 +407,7 @@ public DynamicObject generateAccessor(VirtualFrame frame, DynamicObject module,
final SelfNode self = new SelfNode(getContext(), sourceSection);
final RubyNode accessInstanceVariable;
if (isGetter) {
accessInstanceVariable = new ReadInstanceVariableNode(getContext(), sourceSection, ivar, self, false);
accessInstanceVariable = new ReadInstanceVariableNode(getContext(), sourceSection, ivar, self);
} else {
ReadPreArgumentNode readArgument = new ReadPreArgumentNode(getContext(), sourceSection, 0, MissingArgumentBehaviour.RUNTIME_ERROR);
accessInstanceVariable = new WriteInstanceVariableNode(getContext(), sourceSection, ivar, self, readArgument);
Original file line number Diff line number Diff line change
@@ -65,6 +65,7 @@ public BindingOfCallerNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@TruffleBoundary
@Specialization
public DynamicObject bindingOfCaller() {
/*
@@ -88,6 +89,10 @@ public MaterializedFrame visitFrame(FrameInstance frameInstance) {

});

if (frame == null) {
return nil();
}

return BindingNodes.createBinding(getContext(), frame);
}

@@ -100,6 +105,7 @@ public SourceOfCallerNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@TruffleBoundary
@Specialization
public DynamicObject sourceOfCaller() {
final Memo<Integer> frameCount = new Memo<>(0);
@@ -118,6 +124,10 @@ public String visitFrame(FrameInstance frameInstance) {

});

if (source == null) {
return nil();
}

return createString(StringOperations.encodeByteList(source, UTF8Encoding.INSTANCE));
}

Original file line number Diff line number Diff line change
@@ -14,25 +14,19 @@
import com.oracle.truffle.api.source.SourceSection;
import org.jcodings.specific.UTF8Encoding;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.ThreadLocalObjectNode;
import org.jruby.truffle.nodes.dispatch.CallDispatchHeadNode;
import org.jruby.truffle.nodes.dispatch.DispatchHeadNodeFactory;
import org.jruby.truffle.nodes.objects.ReadInstanceVariableNode;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.core.StringOperations;
import org.jruby.truffle.runtime.layouts.Layouts;
import org.jruby.util.StringSupport;

public class ReadLastBacktraceNode extends RubyNode {

@Child private ReadInstanceVariableNode getLastExceptionNode;
@Child private ReadThreadLocalGlobalVariableNode getLastExceptionNode;
@Child private CallDispatchHeadNode getBacktraceNode;

public ReadLastBacktraceNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
getLastExceptionNode = new ReadInstanceVariableNode(getContext(), getSourceSection(), "$!",
new ThreadLocalObjectNode(getContext(), getSourceSection()),
true);
getLastExceptionNode = new ReadThreadLocalGlobalVariableNode(context, sourceSection, "$!");
getBacktraceNode = DispatchHeadNodeFactory.createMethodCall(getContext());
}

Original file line number Diff line number Diff line change
@@ -14,25 +14,21 @@
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.ThreadLocalObjectNode;
import org.jruby.truffle.nodes.dispatch.CallDispatchHeadNode;
import org.jruby.truffle.nodes.dispatch.DispatchHeadNodeFactory;
import org.jruby.truffle.nodes.objects.ReadInstanceVariableNode;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.control.RaiseException;

public class UpdateLastBacktraceNode extends RubyNode {

@Child private RubyNode child;
@Child private ReadInstanceVariableNode getLastExceptionNode;
@Child private ReadThreadLocalGlobalVariableNode getLastExceptionNode;
@Child private CallDispatchHeadNode setBacktraceNode;

public UpdateLastBacktraceNode(RubyContext context, SourceSection sourceSection, RubyNode child) {
super(context, sourceSection);
this.child = child;
getLastExceptionNode = new ReadInstanceVariableNode(getContext(), getSourceSection(), "$!",
new ThreadLocalObjectNode(getContext(), getSourceSection()),
true);
getLastExceptionNode = new ReadThreadLocalGlobalVariableNode(context, sourceSection, "$!");
setBacktraceNode = DispatchHeadNodeFactory.createMethodCall(getContext());
}

Original file line number Diff line number Diff line change
@@ -424,7 +424,7 @@ private static class InteropInstanceVariableReadNode extends InteropNode {
public InteropInstanceVariableReadNode(RubyContext context, SourceSection sourceSection, String name, int labelIndex) {
super(context, sourceSection);
this.name = name;
this.read = new ReadInstanceVariableNode(context, sourceSection, name, new RubyInteropReceiverNode(context, sourceSection), false);
this.read = new ReadInstanceVariableNode(context, sourceSection, name, new RubyInteropReceiverNode(context, sourceSection));
this.labelIndex = labelIndex;
}

Original file line number Diff line number Diff line change
@@ -19,16 +19,16 @@
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.source.SourceSection;
import com.oracle.truffle.api.utilities.BranchProfile;
import com.oracle.truffle.api.utilities.ConditionProfile;

public class ReadInstanceVariableNode extends RubyNode {

@Child private RubyNode receiver;
@Child private ReadHeadObjectFieldNode readNode;

private final BranchProfile primitiveProfile = BranchProfile.create();
private final ConditionProfile objectProfile = ConditionProfile.createBinaryProfile();

public ReadInstanceVariableNode(RubyContext context, SourceSection sourceSection, String name, RubyNode receiver, boolean isGlobal) {
public ReadInstanceVariableNode(RubyContext context, SourceSection sourceSection, String name, RubyNode receiver) {
super(context, sourceSection);
this.receiver = receiver;
readNode = ReadHeadObjectFieldNodeGen.create(name, nil());
@@ -38,10 +38,9 @@ public ReadInstanceVariableNode(RubyContext context, SourceSection sourceSection
public Object execute(VirtualFrame frame) {
final Object receiverObject = receiver.execute(frame);

if (receiverObject instanceof DynamicObject) {
if (objectProfile.profile(receiverObject instanceof DynamicObject)) {
return readNode.execute((DynamicObject) receiverObject);
} else {
primitiveProfile.enter();
return nil();
}
}
Original file line number Diff line number Diff line change
@@ -11,7 +11,6 @@

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.UnexpectedResultException;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.source.SourceSection;

@@ -23,7 +22,6 @@
import org.jruby.truffle.runtime.control.RaiseException;
import org.jruby.truffle.runtime.core.StringOperations;
import org.jruby.truffle.runtime.layouts.Layouts;
import org.jruby.util.StringSupport;

public class WriteInstanceVariableNode extends RubyNode {

@@ -38,66 +36,6 @@ public WriteInstanceVariableNode(RubyContext context, SourceSection sourceSectio
writeNode = WriteHeadObjectFieldNodeGen.create(name);
}

@Override
public int executeInteger(VirtualFrame frame) throws UnexpectedResultException {
final Object object = receiver.execute(frame);

if (object instanceof DynamicObject) {
try {
final int value = rhs.executeInteger(frame);

writeNode.execute((DynamicObject) object, value);
return value;
} catch (UnexpectedResultException e) {
writeNode.execute((DynamicObject) object, e.getResult());
throw e;
}
} else {
CompilerDirectives.transferToInterpreter();
throw new RaiseException(getContext().getCoreLibrary().frozenError(Layouts.MODULE.getFields(getContext().getCoreLibrary().getLogicalClass(object)).getName(), this));
}
}

@Override
public long executeLong(VirtualFrame frame) throws UnexpectedResultException {
final Object object = receiver.execute(frame);

if (object instanceof DynamicObject) {
try {
final long value = rhs.executeLong(frame);

writeNode.execute((DynamicObject) object, value);
return value;
} catch (UnexpectedResultException e) {
writeNode.execute((DynamicObject) object, e.getResult());
throw e;
}
} else {
CompilerDirectives.transferToInterpreter();
throw new RaiseException(getContext().getCoreLibrary().frozenError(Layouts.MODULE.getFields(getContext().getCoreLibrary().getLogicalClass(object)).getName(), this));
}
}

@Override
public double executeDouble(VirtualFrame frame) throws UnexpectedResultException {
final Object object = receiver.execute(frame);

if (object instanceof DynamicObject) {
try {
final double value = rhs.executeDouble(frame);

writeNode.execute((DynamicObject) object, value);
return value;
} catch (UnexpectedResultException e) {
writeNode.execute((DynamicObject) object, e.getResult());
throw e;
}
} else {
CompilerDirectives.transferToInterpreter();
throw new RaiseException(getContext().getCoreLibrary().frozenError(Layouts.MODULE.getFields(getContext().getCoreLibrary().getLogicalClass(object)).getName(), this));
}
}

@Override
public Object execute(VirtualFrame frame) {
final Object object = receiver.execute(frame);
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@
package org.jruby.truffle.nodes.supercall;

import com.oracle.truffle.api.Assumption;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.Specialization;
@@ -81,6 +82,7 @@ protected DynamicObject metaClass(Object object) {
return metaClassNode.executeMetaClass(object);
}

@TruffleBoundary
protected InternalMethod doLookup(InternalMethod currentMethod, DynamicObject selfMetaClass) {
assert RubyGuards.isRubyClass(selfMetaClass);
InternalMethod superMethod = ModuleOperations.lookupSuperMethod(currentMethod, selfMetaClass);
Original file line number Diff line number Diff line change
@@ -1721,7 +1721,7 @@ public RubyNode visitInstVarNode(org.jruby.ast.InstVarNode node) {
}
}

ret = new ReadInstanceVariableNode(context, sourceSection, name, self, false);
ret = new ReadInstanceVariableNode(context, sourceSection, name, self);
return addNewlineIfNeeded(node, ret);
}

0 comments on commit 069685e

Please sign in to comment.