Skip to content

Commit

Permalink
Split up fat ZSuper into an if-nest and thin ZSuper instructions
Browse files Browse the repository at this point in the history
* The only difference between ZSuper and UnresolvedSuper instructions
  is that ZSuper seems to get its block from the frame if one is
  not provided to it explicitly.
  • Loading branch information
subbuss committed Sep 27, 2014
1 parent cab2272 commit f67e618
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 119 deletions.
119 changes: 71 additions & 48 deletions core/src/main/java/org/jruby/ir/IRBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -915,15 +915,13 @@ private void handleNonlocalReturnInMethod(IRScope s) {
addInstr(s, new LabelInstr(rEndLabel));
}

// Wrap call in a rescue handler that catches the IRBreakJump
private void receiveBreakException(IRScope s, Operand block, CallInstr callInstr, int linenumber) {
private Operand receiveBreakException(IRScope s, Operand block, CodeBlock codeBlock, int linenumber) {
// Check if we have to handle a break
if (block == null ||
!(block instanceof WrappedIRClosure) ||
!(((WrappedIRClosure)block).getClosure()).flags.contains(IRFlags.HAS_BREAK_INSTRS)) {
// No protection needed -- add the call and return
addInstr(s, callInstr);
return;
return codeBlock.run();
}

Label rBeginLabel = s.getNewLabel();
Expand All @@ -933,7 +931,7 @@ private void receiveBreakException(IRScope s, Operand block, CallInstr callInstr
// Protected region
addInstr(s, new LabelInstr(rBeginLabel));
addInstr(s, new ExceptionRegionStartMarkerInstr(rescueLabel));
addInstr(s, callInstr);
Variable callResult = (Variable)codeBlock.run();
addInstr(s, new JumpInstr(rEndLabel));
addInstr(s, new ExceptionRegionEndMarkerInstr());

Expand All @@ -944,10 +942,17 @@ private void receiveBreakException(IRScope s, Operand block, CallInstr callInstr

// Handle break using runtime helper
// --> IRRuntimeHelpers.handlePropagatedBreak(context, scope, bj, blockType)
addInstr(s, new RuntimeHelperCall(callInstr.getResult(), HANDLE_PROPAGATE_BREAK, new Operand[]{exc} ));
addInstr(s, new RuntimeHelperCall(callResult, HANDLE_PROPAGATE_BREAK, new Operand[]{exc} ));

// End
addInstr(s, new LabelInstr(rEndLabel));

return callResult;
}

// Wrap call in a rescue handler that catches the IRBreakJump
private void receiveBreakException(final IRScope s, Operand block, final CallInstr callInstr, int linenumber) {
receiveBreakException(s, block, new CodeBlock() { public Operand run() { addInstr(s, callInstr); return callInstr.getResult(); } }, linenumber);
}

public Operand buildCall(CallNode callNode, IRScope s) {
Expand Down Expand Up @@ -3258,7 +3263,6 @@ private Operand buildSuperInstr(IRScope s, Operand block, Operand[] args, int li
// to 'define_method'.
superInstr = new UnresolvedSuperInstr(ret, s.getSelf(), args, block);
}

receiveBreakException(s, block, superInstr, linenumber);
return ret;
}
Expand Down Expand Up @@ -3395,6 +3399,65 @@ public Operand buildZArray(Node node, IRScope s) {
return copyAndReturnValue(s, new Array());
}

private Operand buildZSuperIfNest(final IRScope s, final Operand block, final int linenumber) {
// If we are in a block, we cannot make any assumptions about what args
// the super instr is going to get -- if there were no 'define_method'
// for defining methods, we could guarantee that the super is going to
// receive args from the nearest method the block is embedded in. But,
// in the presence of 'define_method' (and eval and aliasing), all bets
// are off because, any of the intervening block scopes could be a method
// via a define_method call.
//
// Instead, we can actually collect all arguments of all scopes from here
// till the nearest method scope and select the right set at runtime based
// on which one happened to be a method scope. This has the additional
// advantage of making explicit all used arguments.
CodeBlock zsuperBuilder = new CodeBlock() {
public Operand run() {
Variable scopeDepth = s.createTemporaryVariable();
addInstr(s, new ArgScopeDepthInstr(scopeDepth));

Label allDoneLabel = s.getNewLabel();

IRScope superScope = s;
int depthFromSuper = 0;
Label next = null;

// Loop and generate a block for each possible value of depthFromSuper
Variable zsuperResult = s.createTemporaryVariable();
while (superScope instanceof IRClosure) {
// Generate the next set of instructions
if (next != null) addInstr(s, new LabelInstr(next));
next = s.getNewLabel();
addInstr(s, BNEInstr.create(new Fixnum(depthFromSuper), scopeDepth, next));
Operand[] args = adjustVariableDepth(((IRClosure)superScope).getBlockArgs(), depthFromSuper);
addInstr(s, new ZSuperInstr(zsuperResult, s.getSelf(), args, block));
addInstr(s, new JumpInstr(allDoneLabel));

// Move on
superScope = superScope.getLexicalParent();
depthFromSuper++;
}

addInstr(s, new LabelInstr(next));

// If we hit a method, this is known to always succeed
if (superScope instanceof IRMethod) {
Operand[] args = adjustVariableDepth(((IRMethod)superScope).getCallArgs(), depthFromSuper);
addInstr(s, new ZSuperInstr(zsuperResult, s.getSelf(), args, block));
} else {
/* Control should never get here in the runtime */
/* Should we add an exception throw here just in case? */
}

addInstr(s, new LabelInstr(allDoneLabel));
return zsuperResult;
}
};

return receiveBreakException(s, block, zsuperBuilder, linenumber);
}

public Operand buildZSuper(ZSuperNode zsuperNode, IRScope s) {
if (s.isModuleBody()) return buildSuperInScriptBody(s);

Expand All @@ -3409,47 +3472,7 @@ public Operand buildZSuper(ZSuperNode zsuperNode, IRScope s) {
Operand[] args = ((IRMethod)s).getCallArgs();
return buildSuperInstr(s, block, args, linenumber);
} else {
// If we are in a block, we cannot make any assumptions about what args
// the super instr is going to get -- if there were no 'define_method'
// for defining methods, we could guarantee that the super is going to
// receive args from the nearest method the block is embedded in. But,
// in the presence of 'define_method' (and eval and aliasing), all bets
// are off because, any of the intervening block scopes could be a method
// via a define_method call.
//
// Instead, we can actually collect all arguments of all scopes from here
// till the nearest method scope and select the right set at runtime based
// on which one happened to be a method scope. This has the additional
// advantage of making explicit all used arguments.
Variable ret = s.createTemporaryVariable();
List<Integer> argsCount = new ArrayList<Integer>();
List<Operand> allPossibleArgs = new ArrayList<Operand>();
IRScope superScope = s;
int depthFromSuper = 0;
while (superScope instanceof IRClosure) {
Operand[] args = ((IRClosure)superScope).getBlockArgs();

// Accummulate the closure's args
Collections.addAll(allPossibleArgs, adjustVariableDepth(args, depthFromSuper));
// Record args count of the closure
argsCount.add(args.length);
superScope = superScope.getLexicalParent();
depthFromSuper++;
}

if (superScope instanceof IRMethod) {
Operand[] args = ((IRMethod)superScope).getCallArgs();

// Accummulate the method's args
Collections.addAll(allPossibleArgs, adjustVariableDepth(args, depthFromSuper));
// Record args count of the method
argsCount.add(args.length);
}

receiveBreakException(s, block, new ZSuperInstr(ret, s.getSelf(), block,
allPossibleArgs.toArray(new Operand[allPossibleArgs.size()]),
argsCount.toArray(new Integer[argsCount.size()])), linenumber);
return ret;
return buildZSuperIfNest(s, block, linenumber);
}
}

Expand Down
1 change: 1 addition & 0 deletions core/src/main/java/org/jruby/ir/IRVisitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ private void error(Object object) {

// standard instructions
public void AliasInstr(AliasInstr aliasinstr) { error(aliasinstr); }
public void ArgScopeDepthInstr(ArgScopeDepthInstr instr) { error(instr); }
public void AttrAssignInstr(AttrAssignInstr attrassigninstr) { error(attrassigninstr); }
public void BacktickInstr(BacktickInstr instr) { error(instr); }
public void BEQInstr(BEQInstr beqinstr) { error(beqinstr); }
Expand Down
1 change: 1 addition & 0 deletions core/src/main/java/org/jruby/ir/Operation.java
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ public enum Operation {
TRACE(OpFlags.f_is_book_keeping_op | OpFlags.f_is_debug_op | OpFlags.f_has_side_effect),

/** JRuby-impl instructions **/
ARG_SCOPE_DEPTH(0),
BINDING_LOAD(OpFlags.f_is_load),
BINDING_STORE(OpFlags.f_is_store | OpFlags.f_has_side_effect),
BUILD_COMPOUND_ARRAY(OpFlags.f_can_raise_exception),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package org.jruby.ir.instructions;

import org.jruby.ir.IRVisitor;
import org.jruby.ir.Operation;
import org.jruby.ir.operands.Operand;
import org.jruby.ir.operands.Variable;
import org.jruby.ir.transformations.inlining.InlinerInfo;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.DynamicScope;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;

public class ArgScopeDepthInstr extends Instr implements ResultInstr,FixedArityInstr {
private Operand arg;
private Variable result;

public ArgScopeDepthInstr(Variable result) {
super(Operation.ARG_SCOPE_DEPTH);
this.result = result;
}

@Override
public Operand[] getOperands() {
return EMPTY_OPERANDS;
}

@Override
public Variable getResult() {
return result;
}

@Override
public void updateResult(Variable v) {
this.result = v;
}

@Override
public Instr cloneForInlining(InlinerInfo ii) {
return new ArgScopeDepthInstr(ii.getRenamedVariable(result));
}

@Override
public String toString() {
return result + " = " + super.toString();
}

@Override
public void visit(IRVisitor visitor) {
visitor.ArgScopeDepthInstr(this);
}

@Override
public Object interpret(ThreadContext context, StaticScope currScope, DynamicScope currDynScope, IRubyObject self, Object[] temp) {
int i = 0;
while (!currDynScope.getStaticScope().isArgumentScope()) {
currDynScope = currDynScope.getParentScope();
i++;
}
return context.runtime.newFixnum(i);
}
}
61 changes: 4 additions & 57 deletions core/src/main/java/org/jruby/ir/instructions/ZSuperInstr.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,9 @@
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;

import java.util.Arrays;

public class ZSuperInstr extends UnresolvedSuperInstr {
Operand[] allPossibleArgs;
Integer[] argCounts;

// SSS FIXME: receiver is never used -- being passed in only to meet requirements of CallInstr
public ZSuperInstr(Variable result, Operand receiver, Operand closure, Operand[] allPossibleArgs, Integer[] argCounts) {
super(Operation.ZSUPER, result, receiver, allPossibleArgs, closure);
this.allPossibleArgs = allPossibleArgs;
this.argCounts = argCounts;
public ZSuperInstr(Variable result, Operand receiver, Operand[] args, Operand closure) {
super(Operation.ZSUPER, result, receiver, args, closure);
}

@Override
Expand All @@ -36,64 +28,19 @@ public boolean computeScopeFlags(IRScope scope) {

@Override
public Instr cloneForInlining(InlinerInfo ii) {
int numArgs = allPossibleArgs.length;
Operand[] clonedArgs = new Operand[numArgs];
for (int i = 0; i < numArgs; i++) {
clonedArgs[i] = allPossibleArgs[i].cloneForInlining(ii);
}

return new ZSuperInstr(ii.getRenamedVariable(result), getReceiver().cloneForInlining(ii), closure == null ? null : closure.cloneForInlining(ii), clonedArgs, argCounts);
}

@Override
protected IRubyObject[] prepareArguments(ThreadContext context, IRubyObject self, Operand[] arguments, StaticScope currScope, DynamicScope dynamicScope, Object[] temp) {
// Unlike calls, zsuper args are known only at interpret time, not at constructor time.
// So, we cannot use the cached containsArgSplat field from CallBase
return containsArgSplat(arguments) ?
prepareArgumentsComplex(context, self, arguments, currScope, dynamicScope, temp) :
prepareArgumentsSimple(context, self, arguments, currScope, dynamicScope, temp);
return new ZSuperInstr(ii.getRenamedVariable(getResult()), getReceiver().cloneForInlining(ii), cloneCallArgs(ii), closure == null ? null : closure.cloneForInlining(ii));
}

@Override
public Object interpret(ThreadContext context, StaticScope currScope, DynamicScope currDynScope, IRubyObject self, Object[] temp) {
DynamicScope argsDynScope = currDynScope;

// Find args that need to be passed into super
// CON FIXME: Does this need to be done every time?
int i = 0, offset = 0;
while (!argsDynScope.getStaticScope().isArgumentScope()) {
argsDynScope = argsDynScope.getParentScope();
offset += argCounts[i];
i++;
}

int n = argCounts[i];
Operand[] superArgs = new Operand[n];
for (int j = 0; j < n; j++) {
superArgs[j] = allPossibleArgs[offset+j];
}

// Prepare args
IRubyObject[] args = prepareArguments(context, self, superArgs, currScope, currDynScope, temp);

// Prepare block -- fetching from the frame stack, if necessary
IRubyObject[] args = prepareArguments(context, self, getCallArgs(), currScope, currDynScope, temp);
Block block = prepareBlock(context, self, currScope, currDynScope, temp);
if (block == null || !block.isGiven()) block = context.getFrameBlock();

return IRRuntimeHelpers.unresolvedSuper(context, self, args, block);
}

public Integer[] getArgCounts() {
return argCounts;
}

@Override
public void visit(IRVisitor visitor) {
visitor.ZSuperInstr(this);
}

@Override
public String toString() {
return "" + getOperation() + "(" + receiver + ", " + Arrays.toString(getCallArgs()) + ", " + Arrays.toString(argCounts) + (closure == null ? "" : ", &" + closure) + ")";
}
}
11 changes: 2 additions & 9 deletions core/src/main/java/org/jruby/ir/persistence/InstrDecoderMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ public Instr decodeUnresolvedSuperInstr() {
return new UnresolvedSuperInstr(result, receiver, args, closure);
}

public Instr decodeZSuperInstr() {
public Instr decodeZSuperInstr() {
Variable result = d.decodeVariable();
Operand receiver = d.decodeOperand();
boolean hasClosure = d.decodeBoolean();
Expand All @@ -335,13 +335,6 @@ public Instr decodeZSuperInstr() {
args[i] = d.decodeOperand();
}

argsLength = d.decodeInt();
// if (RubyInstanceConfig.IR_READING_DEBUG) System.out.println("ARGS: " + argsLength + ", CLOSURE: " + hasClosure);
Integer[] argCounts = new Integer[argsLength];
for (int i = 0; i < argsLength; i++) {
argCounts[i] = d.decodeInt();
}

return new ZSuperInstr(result, receiver, closure, args, argCounts);
return new ZSuperInstr(result, receiver, args, closure);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -520,11 +520,6 @@ private void encodeZSuperInstr(ZSuperInstr instr) {
for (Operand arg: instr.getCallArgs()) {
e.encode(arg);
}

e.encode(instr.getArgCounts().length);
for (Integer i: instr.getArgCounts()) {
e.encode(i);
}
}

private void encodeTwoOperandBranchInstr(TwoOperandBranchInstr instr) {
Expand Down

0 comments on commit f67e618

Please sign in to comment.