Skip to content

Commit

Permalink
New yieldy stuff.
Browse files Browse the repository at this point in the history
  • Loading branch information
headius committed Jan 8, 2015
1 parent 35be34a commit fa745e7
Show file tree
Hide file tree
Showing 11 changed files with 248 additions and 76 deletions.
97 changes: 46 additions & 51 deletions core/src/main/java/org/jruby/ir/IRBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -312,24 +312,6 @@ public void addInstrAtBeginning(IRScope s, Instr i) {
}
}

// FIXME: This all seems wrong to me. IRClosures can receive explicit closures why are we searching only methods
// and by-passing closures
private Operand getImplicitBlockArg(IRScope s) {
int n = 0;
while (s instanceof IRClosure || s instanceof IRMetaClassBody) {
n++;
s = s.getLexicalParent();
}

if (s != null) {
LocalVariable v = s instanceof IRMethod ? s.getLocalVariable(Variable.BLOCK, 0) : null;

if (v != null) return n == 0 ? v : v.cloneForDepth(n);
}

return manager.getNil();
}

// Emit cloned ensure bodies by walking up the ensure block stack.
// If we have been passed a loop value, only emit bodies that are nested within that loop.
private void emitEnsureBlocks(IRScope s, IRLoop loop) {
Expand Down Expand Up @@ -509,8 +491,8 @@ public Operand buildLambda(LambdaNode node, IRScope s) {
// like the ensure block stack
IRBuilder closureBuilder = newIRBuilder(manager);

// Receive self
closureBuilder.addInstr(closure, new ReceiveSelfInstr(closure.getSelf()));
// Prepare all implicit state (self, frame block, etc)
closureBuilder.prepareImplicitState(closure);

// Set %current_scope = <current-scope>
// Set %current_module = <current-module>
Expand Down Expand Up @@ -1445,7 +1427,7 @@ public Operand run() {
return addResultInstr(scope, new RuntimeHelperCall(scope.createTemporaryVariable(), IS_DEFINED_METHOD,
new Operand[] { scope.getSelf(), new StringLiteral(((VCallNode) node).getName()), Boolean.FALSE}));
case YIELDNODE:
return buildDefinitionCheck(scope, new BlockGivenInstr(scope.createTemporaryVariable(), getImplicitBlockArg(scope)), "yield");
return buildDefinitionCheck(scope, new BlockGivenInstr(scope.createTemporaryVariable(), scope.getImplicitClosureVariable()), "yield");
case ZSUPERNODE:
return addResultInstr(scope, new RuntimeHelperCall(scope.createTemporaryVariable(), IS_DEFINED_SUPER,
new Operand[] { scope.getSelf() } ));
Expand Down Expand Up @@ -1678,6 +1660,8 @@ protected IRMethod defineMethodInner(MethodDefNode defNode, IRMethod method, IRS

addInstr(method, new ReceiveSelfInstr(method.getSelf()));

// Prepare all implicit state (self, frame block, etc)
prepareImplicitState(method);

// These instructions need to be toward the top of the method because they may both be needed for
// processing optional arguments as in def foo(a = Object).
Expand Down Expand Up @@ -1766,23 +1750,6 @@ public void receiveRequiredArg(Node node, IRScope s, int argIndex, boolean post,
}
}

private void receiveClosureArg(BlockArgNode blockVarNode, IRScope s) {
Variable blockVar = null;
if (blockVarNode != null) {
String blockArgName = blockVarNode.getName();
blockVar = s.getNewLocalVariable(blockArgName, 0);
if (s instanceof IRMethod) ((IRMethod)s).addArgDesc(IRMethodArgs.ArgType.block, blockArgName);
addInstr(s, new ReceiveClosureInstr(blockVar));
}

// In addition, store the block argument in an implicit block variable
if (s instanceof IRMethod) {
Variable implicitBlockArg = (Variable)getImplicitBlockArg(s);
if (blockVar == null) addInstr(s, new ReceiveClosureInstr(implicitBlockArg));
else addInstr(s, new CopyInstr(implicitBlockArg, blockVar));
}
}

protected void receiveNonBlockArgs(final ArgsNode argsNode, IRScope s) {
final int numPreReqd = argsNode.getPreCount();
final int numPostReqd = argsNode.getPostCount();
Expand Down Expand Up @@ -1864,10 +1831,26 @@ protected void receiveNonBlockArgs(final ArgsNode argsNode, IRScope s) {
}

protected void receiveBlockArg(final ArgsNode argsNode, IRScope s) {
// For methods, we always receive it (implicitly, if the block arg is not explicit)
// For closures, only if it is explicitly present
// reify to Proc if we have a block arg
BlockArgNode blockArg = argsNode.getBlock();
if (s instanceof IRMethod || blockArg != null) receiveClosureArg(blockArg, s);
if (s instanceof IRMethod || blockArg != null) if (blockArg != null) {
String blockArgName = blockArg.getName();
Variable blockVar = s.getNewLocalVariable(blockArgName, 0);
if (s instanceof IRMethod) ((IRMethod) s).addArgDesc(IRMethodArgs.ArgType.block, blockArgName);
addInstr(s, new ReifyClosureInstr(s.getImplicitClosureVariable(), blockVar));
}
}

private void prepareImplicitState(IRScope s) {
// Receive self
addInstr(s, new ReceiveSelfInstr(s.getSelf()));

// load block into temporary variable
if (s instanceof IRMethod) {
addInstr(s, new LoadImplicitClosureInstr(s.getImplicitClosureVariable()));
} else {
addInstr(s, new LoadFrameClosureInstr(s.getImplicitClosureVariable()));
}
}

public void receiveArgs(final ArgsNode argsNode, IRScope s) {
Expand Down Expand Up @@ -2023,7 +2006,7 @@ private void handleBreakAndReturnsInLambdas(IRClosure s) {
addInstr(s, new LabelInstr(rEndLabel));
}

public void receiveMethodArgs(final ArgsNode argsNode, IRScope s) {
public void receiveMethodArgs(final ArgsNode argsNode, IRMethod s) {
receiveArgs(argsNode, s);
}

Expand Down Expand Up @@ -2348,8 +2331,8 @@ public Operand buildForIter(final ForNode forNode, IRScope s) {
// like the ensure block stack
IRBuilder forBuilder = newIRBuilder(manager);

// Receive self
forBuilder.addInstr(closure, new ReceiveSelfInstr(closure.getSelf()));
// Prepare all implicit state (self, frame block, etc)
forBuilder.prepareImplicitState(closure);

// Build args
Node varNode = forNode.getVarNode();
Expand Down Expand Up @@ -2499,8 +2482,15 @@ public Operand buildIter(final IterNode iterNode, IRScope s) {
// like the ensure block stack
IRBuilder closureBuilder = newIRBuilder(manager);

// Receive self
closureBuilder.addInstr(closure, new ReceiveSelfInstr(closure.getSelf()));
// Prepare all implicit state (self, frame block, etc)
closureBuilder.prepareImplicitState(closure);

// load block into temporary variable
if (s instanceof IRMethod) {
addInstr(s, new LoadImplicitClosureInstr(s.getImplicitClosureVariable()));
} else {
addInstr(s, new LoadFrameClosureInstr(s.getImplicitClosureVariable()));
}

// Build args
if (iterNode.getVarNode().getNodeType() != null) closureBuilder.receiveBlockArgs(iterNode, closure);
Expand Down Expand Up @@ -3249,7 +3239,10 @@ public IRScriptBody buildRoot(RootNode rootNode) {

// Top-level script!
IRScriptBody script = new IRScriptBody(manager, file, staticScope);
addInstr(script, new ReceiveSelfInstr(script.getSelf()));

// Prepare all implicit state (self, frame block, etc)
prepareImplicitState(script);

// Set %current_scope = <current-scope>
// Set %current_module = <current-module>
addInstr(script, new CopyInstr(script.getCurrentScopeVariable(), CURRENT_SCOPE[0]));
Expand Down Expand Up @@ -3301,7 +3294,7 @@ public Operand buildSuper(SuperNode superNode, IRScope s) {

Operand[] args = setupCallArgs(superNode.getArgsNode(), s);
Operand block = setupCallClosure(superNode.getIterNode(), s);
if (block == null) block = getImplicitBlockArg(s);
if (block == null) block = s.getImplicitClosureVariable();
return buildSuperInstr(s, block, args);
}

Expand Down Expand Up @@ -3418,7 +3411,7 @@ public Operand buildYield(YieldNode node, IRScope s) {
}

Variable ret = s.createTemporaryVariable();
addInstr(s, new YieldInstr(ret, getImplicitBlockArg(s), build(argNode, s), unwrap));
addInstr(s, new YieldInstr(ret, s.getImplicitClosureVariable(), build(argNode, s), unwrap));
return ret;
}

Expand Down Expand Up @@ -3490,7 +3483,7 @@ public Operand buildZSuper(ZSuperNode zsuperNode, IRScope s) {
if (s.isModuleBody()) return buildSuperInScriptBody(s);

Operand block = setupCallClosure(zsuperNode.getIterNode(), s);
if (block == null) block = getImplicitBlockArg(s);
if (block == null) block = s.getImplicitClosureVariable();

// Enebo:ZSuper in for (or nested for) can be statically resolved like method but it needs to fixup depth.
if (s instanceof IRMethod) {
Expand Down Expand Up @@ -3527,7 +3520,9 @@ private Operand buildModuleOrClassBody(IRScope parent, Variable moduleVar, IRMod
bodyBuilder.addInstr(body, new TraceInstr(RubyEvent.CLASS, null, body.getFileName(), linenumber));
}

bodyBuilder.addInstr(body, new ReceiveSelfInstr(body.getSelf())); // %self
// Prepare all implicit state (self, frame block, etc)
bodyBuilder.prepareImplicitState(body);

bodyBuilder.addInstr(body, new CopyInstr(body.getCurrentScopeVariable(), CURRENT_SCOPE[0])); // %scope
bodyBuilder.addInstr(body, new CopyInstr(body.getCurrentModuleVariable(), SCOPE_MODULE[0])); // %module
// Create a new nested builder to ensure this gets its own IR builder state
Expand Down
10 changes: 10 additions & 0 deletions core/src/main/java/org/jruby/ir/IRScope.java
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ public abstract class IRScope implements ParseResult {

private IRManager manager;

private TemporaryVariable implicitClosureVariable;

// Used by cloning code
protected IRScope(IRScope s, IRScope lexicalParent) {
this.lexicalParent = lexicalParent;
Expand Down Expand Up @@ -896,6 +898,14 @@ public void setTemporaryVariableCount(int count) {
temporaryVariableIndex = count + 1;
}

public TemporaryVariable getImplicitClosureVariable() {
if (implicitClosureVariable == null) {
return implicitClosureVariable = createTemporaryVariable();
}

return implicitClosureVariable;
}

public TemporaryLocalVariable getNewUnboxedVariable(Class type) {
TemporaryVariableType varType;
if (type == Float.class) {
Expand Down
4 changes: 3 additions & 1 deletion core/src/main/java/org/jruby/ir/IRVisitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ private void error(Object object) {
public void LexicalSearchConstInstr(LexicalSearchConstInstr lexicalsearchconstinstr) { error(lexicalsearchconstinstr); }
public void LineNumberInstr(LineNumberInstr linenumberinstr) { error(linenumberinstr); }
public void LoadLocalVarInstr(LoadLocalVarInstr loadlocalvarinstr) { error(loadlocalvarinstr); }
public void LoadImplicitClosure(LoadImplicitClosureInstr loadimplicitclosureinstr) { error(loadimplicitclosureinstr); }
public void LoadFrameClosure(LoadFrameClosureInstr loadframeclosureinstr) { error(loadframeclosureinstr); }
public void Match2Instr(Match2Instr match2instr) { error(match2instr); }
public void Match3Instr(Match3Instr match3instr) { error(match3instr); }
public void MatchInstr(MatchInstr matchinstr) { error(matchinstr); }
Expand All @@ -95,7 +97,7 @@ private void error(Object object) {
public void PushFrameInstr(PushFrameInstr pushframeinstr) { error(pushframeinstr); }
public void RaiseArgumentErrorInstr(RaiseArgumentErrorInstr raiseargumenterrorinstr) { error(raiseargumenterrorinstr); }
public void RaiseRequiredKeywordArgumentErrorInstr(RaiseRequiredKeywordArgumentError instr) { error(instr); }
public void ReceiveClosureInstr(ReceiveClosureInstr receiveclosureinstr) { error(receiveclosureinstr); }
public void ReifyClosureInstr(ReifyClosureInstr reifyclosureinstr) { error(reifyclosureinstr); }
public void ReceiveRubyExceptionInstr(ReceiveRubyExceptionInstr receiveexceptioninstr) { error(receiveexceptioninstr); }
public void ReceiveJRubyExceptionInstr(ReceiveJRubyExceptionInstr receiveexceptioninstr) { error(receiveexceptioninstr); }
public void ReceiveKeywordArgInstr(ReceiveKeywordArgInstr receiveKeywordArgInstr) { error(receiveKeywordArgInstr); }
Expand Down
6 changes: 5 additions & 1 deletion core/src/main/java/org/jruby/ir/Operation.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,13 @@ public enum Operation {
RECV_KW_REST_ARG(OpFlags.f_is_arg_receive),
RECV_REST_ARG(OpFlags.f_is_arg_receive),
RECV_OPT_ARG(OpFlags.f_is_arg_receive),
RECV_CLOSURE(OpFlags.f_is_arg_receive),
RECV_RUBY_EXC(OpFlags.f_is_arg_receive),
RECV_JRUBY_EXC(OpFlags.f_is_arg_receive),
LOAD_IMPLICT_CLOSURE(OpFlags.f_is_arg_receive),
LOAD_FRAME_CLOSURE(OpFlags.f_is_arg_receive),

/** Instruction to reify an passed-in block to a Proc for def foo(&b) */
REIFY_CLOSURE(0),

/* By default, call instructions cannot be deleted even if their results
* aren't used by anyone unless we know more about what the call is,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package org.jruby.ir.instructions;

import org.jruby.ir.IRFlags;
import org.jruby.ir.IRScope;
import org.jruby.ir.IRVisitor;
import org.jruby.ir.Operation;
Expand All @@ -12,13 +11,13 @@
import org.jruby.ir.transformations.inlining.SimpleCloneInfo;

/* Receive the closure argument (either implicit or explicit in Ruby source code) */
public class ReceiveClosureInstr extends Instr implements ResultInstr, FixedArityInstr {
public class LoadFrameClosureInstr extends Instr implements ResultInstr, FixedArityInstr {
private Variable result;

public ReceiveClosureInstr(Variable result) {
super(Operation.RECV_CLOSURE);
public LoadFrameClosureInstr(Variable result) {
super(Operation.LOAD_FRAME_CLOSURE);

assert result != null: "ReceiveClosureInstr result is null";
assert result != null: "LoadFrameClosureInstr result is null";

this.result = result;
}
Expand All @@ -38,15 +37,9 @@ public void updateResult(Variable v) {
this.result = v;
}

@Override
public boolean computeScopeFlags(IRScope scope) {
scope.getFlags().add(IRFlags.RECEIVES_CLOSURE_ARG);
return true;
}

@Override
public Instr clone(CloneInfo info) {
if (info instanceof SimpleCloneInfo) return new ReceiveClosureInstr(info.getRenamedVariable(result));
if (info instanceof SimpleCloneInfo) return new LoadFrameClosureInstr(info.getRenamedVariable(result));

// SSS FIXME: This code below is for inlining and is untested.

Expand All @@ -61,6 +54,6 @@ public Instr clone(CloneInfo info) {

@Override
public void visit(IRVisitor visitor) {
visitor.ReceiveClosureInstr(this);
visitor.LoadFrameClosure(this);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package org.jruby.ir.instructions;

import org.jruby.ir.IRScope;
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.operands.WrappedIRClosure;
import org.jruby.ir.transformations.inlining.CloneInfo;
import org.jruby.ir.transformations.inlining.InlineCloneInfo;
import org.jruby.ir.transformations.inlining.SimpleCloneInfo;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.Block;
import org.jruby.runtime.DynamicScope;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;

/* Receive the closure argument (either implicit or explicit in Ruby source code) */
public class LoadImplicitClosureInstr extends Instr implements ResultInstr, FixedArityInstr {
private Variable result;

public LoadImplicitClosureInstr(Variable result) {
super(Operation.LOAD_IMPLICT_CLOSURE);

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

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 clone(CloneInfo info) {
if (info instanceof SimpleCloneInfo) return new LoadImplicitClosureInstr(info.getRenamedVariable(result));

// SSS FIXME: This code below is for inlining and is untested.

InlineCloneInfo ii = (InlineCloneInfo) info;

// SSS FIXME: This is not strictly correct -- we have to wrap the block into an
// operand type that converts the static code block to a proc which is a closure.
if (ii.getCallClosure() instanceof WrappedIRClosure) return NopInstr.NOP;

return new CopyInstr(ii.getRenamedVariable(result), ii.getCallClosure());
}

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

0 comments on commit fa745e7

Please sign in to comment.