Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
WIP: args boxing (0/1 arg)
Browse files Browse the repository at this point in the history
subbuss committed Nov 29, 2013
1 parent 21426ab commit 8becd73
Showing 9 changed files with 120 additions and 59 deletions.
Original file line number Diff line number Diff line change
@@ -29,7 +29,7 @@ public class InterpretedIRMethod extends DynamicMethod implements IRMethodArgs,
private final IRScope method;
private Arity arity;
boolean displayedCFG = false; // FIXME: Remove when we find nicer way of logging CFG

public InterpretedIRMethod(IRScope method, Visibility visibility, RubyModule implementationClass) {
super(implementationClass, visibility, CallConfiguration.FrameNoneScopeNone);
this.method = method;
@@ -41,7 +41,7 @@ public InterpretedIRMethod(IRScope method, Visibility visibility, RubyModule imp
public InterpretedIRMethod(IRScope method, RubyModule implementationClass) {
this(method, Visibility.PRIVATE, implementationClass);
}

public IRScope getIRMethod() {
return method;
}
@@ -62,8 +62,7 @@ public Arity getArity() {
return this.arity;
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args, Block block) {
private void prepareMethod(String name) {
// SSS FIXME: Move this out of here to some other place?
// Prepare method if not yet done so we know if the method has an explicit/implicit call protocol
if (method.getInstrsForInterpretation() == null) method.prepareForInterpretation(false);
@@ -80,15 +79,63 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz
displayedCFG = true;
}
}
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, Block block) {
prepareMethod(name);
if (method.hasExplicitCallProtocol()) {
return Interpreter.INTERPRET_METHOD(context, this, self, name, 0, null, null, block, null, false);
} else {
try {
// update call stacks (push: frame, class, scope, etc.)
context.preMethodFrameAndScope(getImplementationClass(), name, self, block, method.getStaticScope());
context.setCurrentVisibility(getVisibility());
return Interpreter.INTERPRET_METHOD(context, this, self, name, 0, null, null, block, null, false);
} finally {
// update call stacks (pop: ..)
context.popFrame();
context.popRubyClass();
context.popScope();
}
}
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, Block block) {
prepareMethod(name);
if (method.hasExplicitCallProtocol()) {
return Interpreter.INTERPRET_METHOD(context, this, self, name, 1, arg0, null, block, null, false);
} else {
try {
// update call stacks (push: frame, class, scope, etc.)
context.preMethodFrameAndScope(getImplementationClass(), name, self, block, method.getStaticScope());
context.setCurrentVisibility(getVisibility());
return Interpreter.INTERPRET_METHOD(context, this, self, name, 1, arg0, null, block, null, false);
} finally {
// update call stacks (pop: ..)
context.popFrame();
context.popRubyClass();
context.popScope();
}
}
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args, Block block) {
prepareMethod(name);

int n = args.length;
IRubyObject arg0 = n >= 1 ? args[0] : null;

if (method.hasExplicitCallProtocol()) {
return Interpreter.INTERPRET_METHOD(context, this, self, name, args, block, null, false);
return Interpreter.INTERPRET_METHOD(context, this, self, name, n, arg0, args, block, null, false);
} else {
try {
// update call stacks (push: frame, class, scope, etc.)
context.preMethodFrameAndScope(getImplementationClass(), name, self, block, method.getStaticScope());
context.setCurrentVisibility(getVisibility());
return Interpreter.INTERPRET_METHOD(context, this, self, name, args, block, null, false);
return Interpreter.INTERPRET_METHOD(context, this, self, name, n, arg0, args, block, null, false);
} finally {
// update call stacks (pop: ..)
context.popFrame();
Original file line number Diff line number Diff line change
@@ -59,7 +59,7 @@ public Object interpret(ThreadContext context, DynamicScope currDynScope, IRubyO
InterpretedIRMethod bodyMethod = (InterpretedIRMethod)moduleBody.retrieve(context, self, currDynScope, temp);
RubyModule implClass = bodyMethod.getImplementationClass();
// SSS FIXME: Rather than pass the block implicitly, should we add %block as another operand to ProcessModuleBody, DefineModule instrs?
return bodyMethod.call(context, implClass, implClass, "", new IRubyObject[]{}, block);
return bodyMethod.call(context, implClass, implClass, "", block);
}

@Override
Original file line number Diff line number Diff line change
@@ -44,7 +44,11 @@ public String toString() {
return super.toString() + "(" + argIndex + ")";
}

public IRubyObject receiveArg(ThreadContext context, int kwArgHashCount, IRubyObject[] args) {
public IRubyObject receiveArg(ThreadContext context, int kwArgHashCount, int numArgs, IRubyObject arg0, IRubyObject[] args) {
throw new RuntimeException("ReceiveArgBase.interpret called! " + this.getClass().getName() + " does not define receiveArg");
}

public static IRubyObject fetchArgFromArgs(int index, IRubyObject arg0, IRubyObject[] args) {
return (index == 0) ? arg0 : args[index];
}
}
Original file line number Diff line number Diff line change
@@ -26,16 +26,16 @@ public String toString() {
}

@Override
public IRubyObject receiveArg(ThreadContext context, int kwArgHashCount, IRubyObject[] args) {
public IRubyObject receiveArg(ThreadContext context, int kwArgHashCount, int numArgs, IRubyObject arg0, IRubyObject[] args) {
if (kwArgHashCount == 0) {
return UndefinedValue.UNDEFINED;
} else {
RubyHash lastArg = (RubyHash)args[args.length - 1];
if (numUsedArgs == args.length) {
if (numUsedArgs == numArgs) {
/* throw ArgumentError */
Arity.raiseArgumentError(context.getRuntime(), args.length-1, numUsedArgs, -1);
Arity.raiseArgumentError(context.getRuntime(), numArgs-1, numUsedArgs, -1);
}

RubyHash lastArg = (RubyHash)ReceiveArgBase.fetchArgFromArgs(numArgs - 1, arg0, args);
// If the key exists in the hash, delete and return it.
RubySymbol argName = context.getRuntime().newSymbol(getResult().getName());
if (lastArg.fastARef(argName) != null) {
Original file line number Diff line number Diff line change
@@ -25,16 +25,16 @@ public String toString() {
}

@Override
public IRubyObject receiveArg(ThreadContext context, int kwArgHashCount, IRubyObject[] args) {
public IRubyObject receiveArg(ThreadContext context, int kwArgHashCount, int numArgs, IRubyObject arg0, IRubyObject[] args) {
if (kwArgHashCount == 0) {
return RubyHash.newSmallHash(context.getRuntime());
} else {
if (numUsedArgs == args.length) {
if (numUsedArgs == numArgs) {
/* throw ArgumentError */
Arity.raiseArgumentError(context.getRuntime(), args.length-1, numUsedArgs, -1);
Arity.raiseArgumentError(context.getRuntime(), numArgs - 1, numUsedArgs, -1);
}

return args[args.length - 1];
return ReceiveArgBase.fetchArgFromArgs(numArgs - 1, arg0, args);
}
}
}
Original file line number Diff line number Diff line change
@@ -50,11 +50,15 @@ public Instr cloneForBlockCloning(InlinerInfo ii) {
return new ReceiveOptArgInstr(ii.getRenamedVariable(result), numUsedArgs, argOffset, optArgIndex);
}

public IRubyObject receiveOptArg(IRubyObject[] args, int kwArgHashCount) {
public IRubyObject receiveOptArg(int numArgs, IRubyObject arg0, IRubyObject[] args, int kwArgHashCount) {
// Added this copy for code clarity
// argIndex is relative to start of opt args and not the start of arg array
int optArgIndex = this.argIndex;
return (optArgIndex + numUsedArgs + kwArgHashCount < args.length ? args[argOffset + optArgIndex] : UndefinedValue.UNDEFINED);
if (optArgIndex + numUsedArgs + kwArgHashCount < numArgs) {
return ReceiveArgBase.fetchArgFromArgs(argOffset+optArgIndex, arg0, args);
} else {
return UndefinedValue.UNDEFINED;
}
}

@Override
Original file line number Diff line number Diff line change
@@ -54,13 +54,13 @@ public Instr cloneForBlockCloning(InlinerInfo ii) {
return new ReceivePostReqdArgInstr(ii.getRenamedVariable(result), argIndex, preReqdArgsCount, postReqdArgsCount);
}

public IRubyObject receivePostReqdArg(IRubyObject[] args, int kwArgHashCount) {
int n = args.length;
int remaining = n - preReqdArgsCount - kwArgHashCount;
public IRubyObject receivePostReqdArg(int numArgs, IRubyObject arg0, IRubyObject[] args, int kwArgHashCount) {
int remaining = numArgs - preReqdArgsCount - kwArgHashCount;
if (remaining <= argIndex) {
return null; // For blocks!
} else {
return (remaining > postReqdArgsCount) ? args[n - postReqdArgsCount - kwArgHashCount + argIndex] : args[preReqdArgsCount + argIndex];
int index = (remaining > postReqdArgsCount) ? numArgs - postReqdArgsCount - kwArgHashCount + argIndex : preReqdArgsCount + argIndex;
return ReceiveArgBase.fetchArgFromArgs(index, arg0, args);
}
}

Original file line number Diff line number Diff line change
@@ -42,13 +42,17 @@ public Instr cloneForBlockCloning(InlinerInfo ii) {
private IRubyObject[] NO_PARAMS = new IRubyObject[0];

@Override
public IRubyObject receiveArg(ThreadContext context, int kwArgLoss, IRubyObject[] parameters) {
public IRubyObject receiveArg(ThreadContext context, int kwArgLoss, int numArgs, IRubyObject arg0, IRubyObject[] parameters) {
IRubyObject[] args;
int numAvailableArgs = parameters.length - numUsedArgs - kwArgLoss;
int numAvailableArgs = numArgs - numUsedArgs - kwArgLoss;
if (numAvailableArgs <= 0) {
args = NO_PARAMS;
} else {
args = new IRubyObject[numAvailableArgs];
// SSS: Quick simple hack
if (parameters == null) {
parameters = new IRubyObject[] { arg0 };
}
System.arraycopy(parameters, argIndex, args, 0, numAvailableArgs);
}

72 changes: 37 additions & 35 deletions core/src/main/java/org/jruby/ir/interpreter/Interpreter.java
Original file line number Diff line number Diff line change
@@ -193,7 +193,7 @@ public static IRubyObject interpret(Ruby runtime, Node rootNode, IRubyObject sel
try {
runBeginEndBlocks(root.getBeginBlocks(), context, self, null); // FIXME: No temp vars yet...not needed?
InterpretedIRMethod method = new InterpretedIRMethod(root, currModule);
IRubyObject rv = method.call(context, self, currModule, "(root)", IRubyObject.NULL_ARRAY);
IRubyObject rv = method.call(context, self, currModule, "(root)");
runBeginEndBlocks(root.getEndBlocks(), context, self, null); // FIXME: No temp vars yet...not needed?
if ((IRRuntimeHelpers.isDebug() || IRRuntimeHelpers.inProfileMode()) && interpInstrsCount > 10000) {
LOG.info("-- Interpreted instructions: {}", interpInstrsCount);
@@ -501,29 +501,32 @@ private static void updateCallSite(Instr instr, IRScope scope, Integer scopeVers
}
}

private static void receiveArg(ThreadContext context, Instr i, Operation operation, IRubyObject[] args, int kwArgHashCount, DynamicScope currDynScope, Object[] temp, Object exception, Block block) {
private static void receiveArg(ThreadContext context, Instr i, Operation operation, int numArgs, IRubyObject arg0, IRubyObject[] args, int kwArgHashCount, DynamicScope currDynScope, Object[] temp, Block block, Object exception) {
// For all of the RECV_*_ARG cases, numArgs > 0 guaranteed
Object result = null;
ResultInstr instr = (ResultInstr)i;
switch(operation) {
case RECV_PRE_REQD_ARG:
int argIndex = ((ReceivePreReqdArgInstr)instr).getArgIndex();
result = ((argIndex + kwArgHashCount) < args.length) ? args[argIndex] : context.nil; // SSS FIXME: This check is only required for closures, not methods
setResult(temp, currDynScope, instr.getResult(), result);
return;
case RECV_CLOSURE:
result = (block == Block.NULL_BLOCK) ? context.nil : context.runtime.newProc(Block.Type.PROC, block);
// SSS FIXME: This check is only required for closures, not methods
result = argIndex + kwArgHashCount < numArgs ? ReceiveArgBase.fetchArgFromArgs(argIndex, arg0, args)
: context.nil;
setResult(temp, currDynScope, instr.getResult(), result);
return;
case RECV_OPT_ARG:
result = ((ReceiveOptArgInstr)instr).receiveOptArg(args, kwArgHashCount);
result = ((ReceiveOptArgInstr)instr).receiveOptArg(numArgs, arg0, args, kwArgHashCount);
// For blocks, missing arg translates to nil
setResult(temp, currDynScope, instr.getResult(), result);
return;
case RECV_POST_REQD_ARG:
result = ((ReceivePostReqdArgInstr)instr).receivePostReqdArg(args, kwArgHashCount);
result = ((ReceivePostReqdArgInstr)instr).receivePostReqdArg(numArgs, arg0, args, kwArgHashCount);
// For blocks, missing arg translates to nil
setResult(temp, currDynScope, instr.getResult(), result == null ? context.nil : result);
return;
case RECV_CLOSURE:
result = (block == Block.NULL_BLOCK) ? context.nil : context.runtime.newProc(Block.Type.PROC, block);
setResult(temp, currDynScope, instr.getResult(), result);
return;
case RECV_EXCEPTION: {
ReceiveExceptionInstr rei = (ReceiveExceptionInstr)instr;
// FIXME: HACK for now .. make semantics explicit rather
@@ -540,11 +543,11 @@ private static void receiveArg(ThreadContext context, Instr i, Operation operati
Helpers.throwException((Throwable)exception);
}
result = (exception instanceof RaiseException && rei.checkType) ? ((RaiseException)exception).getException() : exception;
setResult(temp, currDynScope, instr.getResult(), result);
return;
setResult(temp, currDynScope, ((ResultInstr)instr).getResult(), result);
break;
}
default:
result = ((ReceiveArgBase)instr).receiveArg(context, kwArgHashCount, args);
result = ((ReceiveArgBase)instr).receiveArg(context, kwArgHashCount, numArgs, arg0, args);
setResult(temp, currDynScope, instr.getResult(), result);
return;
}
@@ -642,7 +645,7 @@ private static void processBookKeepingOp(ThreadContext context, Instr instr, Ope
}

private static IRubyObject interpret(ThreadContext context, IRubyObject self,
IRScope scope, Visibility visibility, RubyModule implClass, IRubyObject[] args, Block block, Block.Type blockType) {
IRScope scope, Visibility visibility, RubyModule implClass, int numArgs, IRubyObject arg0, IRubyObject[] args, Block block, Block.Type blockType) {
Instr[] instrs = scope.getInstrsForInterpretation();

// The base IR may not have been processed yet
@@ -654,7 +657,7 @@ private static IRubyObject interpret(ThreadContext context, IRubyObject self,
int ipc = 0;
Instr instr = null;
Object exception = null;
int kwArgHashCount = (scope.receivesKeywordArgs() && args[args.length - 1] instanceof RubyHash) ? 1 : 0;
int kwArgHashCount = (scope.receivesKeywordArgs() && ReceiveArgBase.fetchArgFromArgs(numArgs-1, arg0, args) instanceof RubyHash) ? 1 : 0;
DynamicScope currDynScope = context.getCurrentScope();

// Counter tpCount = null;
@@ -687,24 +690,21 @@ private static IRubyObject interpret(ThreadContext context, IRubyObject self,

try {
switch (operation.opClass) {
case ARG_OP: {
receiveArg(context, instr, operation, args, kwArgHashCount, currDynScope, temp, exception, block);
case ARG_OP:
receiveArg(context, instr, operation, numArgs, arg0, args, kwArgHashCount, currDynScope, temp, block, exception);
break;
}
case BRANCH_OP: {
case BRANCH_OP:
if (operation == Operation.JUMP) {
ipc = ((JumpInstr)instr).getJumpTarget().getTargetPC();
} else {
ipc = instr.interpretAndGetNewIPC(context, currDynScope, self, temp, ipc);
}
break;
}
case CALL_OP: {
case CALL_OP:
if (profile) updateCallSite(instr, scope, scopeVersion);
processCall(context, instr, operation, scope, currDynScope, temp, self, block, blockType);
break;
}
case BOOK_KEEPING_OP: {
case BOOK_KEEPING_OP:
if (operation == Operation.PUSH_BINDING) {
// SSS NOTE: Method scopes only!
//
@@ -713,35 +713,32 @@ private static IRubyObject interpret(ThreadContext context, IRubyObject self,
currDynScope = DynamicScope.newDynamicScope(scope.getStaticScope());
context.pushScope(currDynScope);
} else {
processBookKeepingOp(context, instr, operation, scope, args.length, self, block, implClass, visibility, profile);
processBookKeepingOp(context, instr, operation, scope, numArgs, self, block, implClass, visibility, profile);
}
break;
}
case OTHER_OP: {
Object result = null;
switch(operation) {
// --------- Return flavored instructions --------
case RETURN:
return (IRubyObject)retrieveOp(((ReturnBase)instr).getReturnValue(), context, self, currDynScope, temp);
case BREAK: {
BreakInstr bi = (BreakInstr)instr;
IRubyObject rv = (IRubyObject)bi.getReturnValue().retrieve(context, self, currDynScope, temp);
// This also handles breaks in lambdas -- by converting them to a return
return IRRuntimeHelpers.initiateBreak(context, scope, bi.getScopeToReturnTo().getScopeId(), rv, blockType);
}
case RETURN: {
return (IRubyObject)retrieveOp(((ReturnBase)instr).getReturnValue(), context, self, currDynScope, temp);
}
case NONLOCAL_RETURN: {
NonlocalReturnInstr ri = (NonlocalReturnInstr)instr;
IRubyObject rv = (IRubyObject)retrieveOp(ri.getReturnValue(), context, self, currDynScope, temp);
ipc = n;
// If not in a lambda, check if this was a non-local return
if (!IRRuntimeHelpers.inLambda(blockType)) {
IRRuntimeHelpers.initiateNonLocalReturn(context, scope, ri.methodToReturnFrom, rv);
}
return rv;
}

// ---------- Common instruction ---------
// ---------- Common instructions ---------
case COPY: {
CopyInstr c = (CopyInstr)instr;
result = retrieveOp(c.getSource(), context, self, currDynScope, temp);
@@ -775,10 +772,9 @@ private static IRubyObject interpret(ThreadContext context, IRubyObject self,
setResult(temp, currDynScope, instr, result);
break;
}

}
break;
}
}
} catch (Throwable t) {
if (debug) LOG.info("in scope: " + scope + ", caught Java throwable: " + t + "; excepting instr: " + instr);
ipc = scope.getRescuerPC(instr);
@@ -799,26 +795,32 @@ private static IRubyObject interpret(ThreadContext context, IRubyObject self,

public static IRubyObject INTERPRET_EVAL(ThreadContext context, IRubyObject self,
IRScope scope, RubyModule clazz, IRubyObject[] args, String name, Block block, Block.Type blockType) {
int numArgs = args.length;
IRubyObject arg0 = numArgs >= 1 ? args[0] : null;
try {
ThreadContext.pushBacktrace(context, name, scope.getFileName(), context.getLine());
return interpret(context, self, scope, null, clazz, args, block, blockType);
// viz and implClass unused on this path
return interpret(context, self, scope, null, null, numArgs, arg0, args, block, blockType);
} finally {
ThreadContext.popBacktrace(context);
}
}

public static IRubyObject INTERPRET_BLOCK(ThreadContext context, IRubyObject self,
IRScope scope, IRubyObject[] args, String name, Block block, Block.Type blockType) {
int numArgs = args.length;
IRubyObject arg0 = numArgs >= 1 ? args[0] : null;
try {
ThreadContext.pushBacktrace(context, name, scope.getFileName(), context.getLine());
return interpret(context, self, scope, null, null, args, block, blockType);
// viz and implClass unused on this path
return interpret(context, self, scope, null, null, numArgs, arg0, args, block, blockType);
} finally {
ThreadContext.popBacktrace(context);
}
}

public static IRubyObject INTERPRET_METHOD(ThreadContext context, InterpretedIRMethod irMethod,
IRubyObject self, String name, IRubyObject[] args, Block block, Block.Type blockType, boolean isTraceable) {
IRubyObject self, String name, int numArgs, IRubyObject arg0, IRubyObject[] args, Block block, Block.Type blockType, boolean isTraceable) {
Ruby runtime = context.runtime;
IRScope scope = irMethod.getIRMethod();
RubyModule implClass = irMethod.getImplementationClass();
@@ -828,7 +830,7 @@ public static IRubyObject INTERPRET_METHOD(ThreadContext context, InterpretedIRM
try {
if (!syntheticMethod) ThreadContext.pushBacktrace(context, name, scope.getFileName(), context.getLine());
if (isTraceable) methodPreTrace(runtime, context, name, implClass);
return interpret(context, self, scope, viz, implClass, args, block, blockType);
return interpret(context, self, scope, viz, implClass, numArgs, arg0, args, block, blockType);
} finally {
if (isTraceable) {
try {methodPostTrace(runtime, context, name, implClass);}

0 comments on commit 8becd73

Please sign in to comment.