-
Notifications
You must be signed in to change notification settings - Fork 914
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[IR] First pass implementing an explicit call protocol via IR
instructions -- for now, only bindings are explicitly managed for non-closure and non-eval scopes. Frames, etc. will come later.
- Loading branch information
Showing
4 changed files
with
175 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
137 changes: 137 additions & 0 deletions
137
src/org/jruby/ir/passes/AddCallProtocolInstructions.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
package org.jruby.ir.passes; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.ListIterator; | ||
|
||
import org.jruby.ir.IRClosure; | ||
import org.jruby.ir.IRMethod; | ||
import org.jruby.ir.IRModuleBody; | ||
import org.jruby.ir.IRScriptBody; | ||
import org.jruby.ir.IRScope; | ||
import org.jruby.ir.instructions.BreakInstr; | ||
import org.jruby.ir.instructions.Instr; | ||
import org.jruby.ir.instructions.PopBindingInstr; | ||
import org.jruby.ir.instructions.PushBindingInstr; | ||
import org.jruby.ir.instructions.ReceiveExceptionInstr; | ||
import org.jruby.ir.instructions.ReturnBase; | ||
import org.jruby.ir.instructions.ThrowExceptionInstr; | ||
import org.jruby.ir.dataflow.analyses.StoreLocalVarPlacementProblem; | ||
import org.jruby.ir.operands.Label; | ||
import org.jruby.ir.operands.LocalVariable; | ||
import org.jruby.ir.operands.Operand; | ||
import org.jruby.ir.operands.Variable; | ||
import org.jruby.ir.representations.BasicBlock; | ||
import org.jruby.ir.representations.CFG; | ||
|
||
public class AddCallProtocolInstructions extends CompilerPass { | ||
boolean addedInstrs = false; | ||
|
||
public String getLabel() { | ||
return "Add Call Protocol Instructions (push/pop of dyn-scope, frame, impl-class values)"; | ||
} | ||
|
||
public static List<Class<? extends CompilerPass>> DEPENDENCIES = new ArrayList<Class<? extends CompilerPass>>() {{ | ||
add(CFGBuilder.class); | ||
}}; | ||
|
||
@Override | ||
public List<Class<? extends CompilerPass>> getDependencies() { | ||
return DEPENDENCIES; | ||
} | ||
|
||
public Object execute(IRScope scope, Object... data) { | ||
StoreLocalVarPlacementProblem slvpp = (StoreLocalVarPlacementProblem)scope.getDataFlowSolution(StoreLocalVarPlacementProblem.NAME); | ||
|
||
boolean scopeHasLocalVarStores = false; | ||
boolean scopeHasUnrescuedExceptions = false; | ||
|
||
CFG cfg = scope.cfg(); | ||
BasicBlock geb = cfg.getGlobalEnsureBB(); | ||
|
||
if (slvpp != null) { | ||
scopeHasLocalVarStores = slvpp.scopeHasLocalVarStores(); | ||
scopeHasUnrescuedExceptions = slvpp.scopeHasUnrescuedExceptions(); | ||
} 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 = true; | ||
scopeHasUnrescuedExceptions = false; | ||
for (BasicBlock bb: cfg.getBasicBlocks()) { | ||
// SSS FIXME: This is highly conservative. If the bb has an exception raising instr. | ||
// and if we dont have a rescuer, only then do we have unrescued exceptions. | ||
if (cfg.getRescuerBBFor(bb) == null) { | ||
scopeHasUnrescuedExceptions = true; | ||
break; | ||
} | ||
} | ||
} | ||
|
||
// Add explicit 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 ((scope instanceof IRMethod) || (scope instanceof IRScriptBody) || (scope instanceof IRModuleBody)) { | ||
if (scope.bindingHasEscaped() || scope.usesBackrefOrLastline() || scopeHasLocalVarStores) { | ||
// Push | ||
cfg.getEntryBB().addInstr(new PushBindingInstr(scope)); | ||
|
||
// Allocate GEB if necessary for popping binding | ||
if (geb == null && scopeHasLocalVarStores && scopeHasUnrescuedExceptions) { | ||
Variable exc = scope.getNewTemporaryVariable(); | ||
geb = new BasicBlock(cfg, new Label("_GLOBAL_ENSURE_BLOCK")); | ||
geb.addInstr(new ReceiveExceptionInstr(exc)); | ||
geb.addInstr(new ThrowExceptionInstr(exc)); | ||
cfg.addGlobalEnsureBB(geb); | ||
} | ||
|
||
// Pop on all scope-exit paths | ||
BasicBlock exitBB = cfg.getExitBB(); | ||
for (BasicBlock bb: cfg.getBasicBlocks()) { | ||
ListIterator<Instr> instrs = bb.getInstrs().listIterator(); | ||
while (instrs.hasNext()) { | ||
Instr i = instrs.next(); | ||
if ((bb != exitBB) && (i instanceof ReturnBase) || (i instanceof BreakInstr)) { | ||
// Add before the break/return | ||
instrs.previous(); | ||
instrs.add(new PopBindingInstr()); | ||
break; | ||
} | ||
} | ||
|
||
if (bb == exitBB) { | ||
// Last instr could be a return -- so, move iterator one position back | ||
if (instrs.hasPrevious()) instrs.previous(); | ||
instrs.add(new PopBindingInstr()); | ||
} | ||
|
||
if (bb == geb) { | ||
// Add before throw-exception-instr which would be the last instr | ||
instrs.previous(); | ||
instrs.add(new PopBindingInstr()); | ||
} | ||
} | ||
} | ||
|
||
// This scope has an explicit call protocol flag now | ||
scope.setExplicitCallProtocolFlag(true); | ||
} | ||
|
||
// Run on all nested closures. | ||
for (IRClosure c: scope.getClosures()) execute(c); | ||
|
||
// Mark as done | ||
addedInstrs = true; | ||
|
||
return null; | ||
} | ||
|
||
@Override | ||
public Object previouslyRun(IRScope scope) { | ||
return addedInstrs ? new Object() : null; | ||
} | ||
|
||
@Override | ||
public void invalidate(IRScope scope) { | ||
// Cannot add call protocol instructions after we've added them once. | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters