Skip to content

Commit 72dc8b4

Browse files
committed
Merge branch 'master' into non-indy-jit
Conflicts: core/src/main/java/org/jruby/ir/IRScope.java
2 parents 5dda245 + a87c3a5 commit 72dc8b4

16 files changed

+253
-191
lines changed

core/src/main/java/org/jruby/ir/IRFlags.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,20 +41,19 @@ public enum IRFlags {
4141
CAN_RECEIVE_NONLOCAL_RETURNS, // may receive a non-local return during execution
4242
HAS_BREAK_INSTRS, // contains at least one break
4343
HAS_END_BLOCKS, // has an end block. big de-opt flag
44-
HAS_EXPLICIT_CALL_PROTOCOL, // contains call protocol instrs. if so we don't need to manage bindings frame implicitly.
44+
HAS_EXPLICIT_CALL_PROTOCOL, // contains call protocol instrs => we don't need to manage bindings frame implicitly
4545
HAS_LOOPS, // has a loop
4646
HAS_NONLOCAL_RETURNS, // has a non-local return
47-
HAS_OPTIMIZED_TEMPORARY_VARIABLES, // we simplified number of temp vars (before CFG was built). we can only do once.
4847
HAS_UNUSED_IMPLICIT_BLOCK_ARG,// Is %block implicit block arg unused?
4948
RECEIVES_CLOSURE_ARG, // This scope (or parent receives a closure
5049
RECEIVES_KEYWORD_ARGS, // receives keyword args
5150
REQUIRES_DYNSCOPE, // does this scope require a dynamic scope?
52-
USES_BACKREF_OR_LASTLINE, // Since backref ($~) and lastline ($_) vars are allocated space on the dynamic scope.
51+
USES_BACKREF_OR_LASTLINE, // Since backref ($~) and lastline ($_) vars are allocated space on the dynamic scope
5352
USES_EVAL, // calls eval
5453
USES_ZSUPER, // has zsuper instr
5554
REQUIRES_FRAME, // callee may read/write caller's frame elements
5655
REQUIRES_VISIBILITY, // callee may read/write caller's visibility
5756

5857
DYNSCOPE_ELIMINATED, // local var load/stores have been converted to tmp var accesses
59-
REUSE_PARENT_DYNSCOPE, // for clsoures -- reuse parent's dynscope
58+
REUSE_PARENT_DYNSCOPE, // for closures -- reuse parent's dynscope
6059
}

core/src/main/java/org/jruby/ir/IRManager.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@
1717
/**
1818
*/
1919
public class IRManager {
20-
public static String SAFE_COMPILER_PASSES = "LinearizeCFG";
21-
public static String DEFAULT_COMPILER_PASSES = "OptimizeTempVarsPass,LocalOptimizationPass,LinearizeCFG";
22-
public static String DEFAULT_JIT_PASSES = "AddLocalVarLoadStoreInstructions,AddCallProtocolInstructions,EnsureTempsAssigned,LinearizeCFG";
20+
public static String SAFE_COMPILER_PASSES = "";
21+
public static String DEFAULT_COMPILER_PASSES = "OptimizeTempVarsPass,LocalOptimizationPass";
22+
public static String DEFAULT_JIT_PASSES = "AddLocalVarLoadStoreInstructions,AddCallProtocolInstructions,EnsureTempsAssigned";
2323
public static String DEFAULT_INLINING_COMPILER_PASSES = "LocalOptimizationPass";
2424

2525
private int dummyMetaClassCount = 0;

core/src/main/java/org/jruby/ir/IRScope.java

Lines changed: 39 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import org.jruby.ir.passes.CompilerPass;
1212
import org.jruby.ir.passes.CompilerPassScheduler;
1313
import org.jruby.ir.passes.DeadCodeElimination;
14+
import org.jruby.ir.passes.OptimizeDynScopesPass;
1415
import org.jruby.ir.dataflow.analyses.StoreLocalVarPlacementProblem;
1516
import org.jruby.ir.dataflow.analyses.LiveVariablesProblem;
1617
import org.jruby.ir.passes.UnboxingPass;
@@ -105,6 +106,9 @@ public abstract class IRScope implements ParseResult {
105106
/** Map of name -> dataflow problem */
106107
private Map<String, DataFlowProblem> dfProbs;
107108

109+
/** What passes have been run on this scope? */
110+
private List<CompilerPass> executedPasses;
111+
108112
private Instr[] linearizedInstrArray;
109113
private List<BasicBlock> linearizedBBList;
110114
private Map<Integer, Integer> rescueMap;
@@ -163,6 +167,8 @@ protected IRScope(IRScope s, IRScope lexicalParent) {
163167
this.scopeId = globalScopeCount.getAndIncrement();
164168
this.relinearizeCFG = false;
165169

170+
this.executedPasses = new ArrayList<CompilerPass>();
171+
166172
setupLexicalContainment();
167173
}
168174

@@ -208,6 +214,8 @@ public IRScope(IRManager manager, IRScope lexicalParent, String name,
208214
this.scopeId = globalScopeCount.getAndIncrement();
209215
this.relinearizeCFG = false;
210216

217+
this.executedPasses = new ArrayList<CompilerPass>();
218+
211219
setupLexicalContainment();
212220
}
213221

@@ -400,14 +408,6 @@ public boolean isNestedInClosure(IRClosure closure) {
400408
return false;
401409
}
402410

403-
public boolean hasHasOptimizedTemporaryVariables() {
404-
return flags.contains(HAS_OPTIMIZED_TEMPORARY_VARIABLES);
405-
}
406-
407-
public void setHasOptimizedTemporaryVariables() {
408-
flags.add(HAS_OPTIMIZED_TEMPORARY_VARIABLES);
409-
}
410-
411411
public void setHasLoopsFlag() {
412412
flags.add(HAS_LOOPS);
413413
}
@@ -546,6 +546,10 @@ private boolean isUnsafeScope() {
546546
return unsafeScope;
547547
}
548548

549+
public List<CompilerPass> getExecutedPasses() {
550+
return executedPasses;
551+
}
552+
549553
private void runCompilerPasses(List<CompilerPass> passes) {
550554
// SSS FIXME: Why is this again? Document this weirdness!
551555
// Forcibly clear out the shared eval-scope variable allocator each time this method executes
@@ -569,40 +573,28 @@ private void runCompilerPasses(List<CompilerPass> passes) {
569573

570574
CompilerPassScheduler scheduler = getManager().schedulePasses(passes);
571575
for (CompilerPass pass: scheduler) {
572-
if (pass.previouslyRun(this) == null) {
573-
pass.run(this);
574-
}
576+
pass.run(this);
575577
}
576578

577579
if (RubyInstanceConfig.IR_UNBOXING) {
578580
(new UnboxingPass()).run(this);
579581
}
580582
}
581583

582-
private void runDeadCodeAndVarLoadStorePasses() {
583-
// For scopes that don't require a dynamic scope,
584-
// inline-add lvar loads/store to tmp-var loads/stores.
584+
private void optimizeSimpleScopes() {
585+
// For safe scopes that don't require a dynamic scope,
586+
// run DCE since the analysis is less likely to be
587+
// stymied by escaped bindings.
585588
if (!isUnsafeScope() && !flags.contains(REQUIRES_DYNSCOPE)) {
586-
CompilerPass pass;
587-
pass = new DeadCodeElimination();
588-
if (pass.previouslyRun(this) == null) {
589-
pass.run(this);
590-
}
591-
592-
// This will run the simplified version of the pass
593-
// that doesn't require dataflow analysis and hence
594-
// can run on closures independent of enclosing scopes.
595-
pass = new AddLocalVarLoadStoreInstructions();
596-
if (pass.previouslyRun(this) == null) {
597-
((AddLocalVarLoadStoreInstructions)pass).eliminateLocalVars(this);
598-
setDataFlowSolution(StoreLocalVarPlacementProblem.NAME, new StoreLocalVarPlacementProblem());
599-
setDataFlowSolution(LiveVariablesProblem.NAME, null);
600-
}
589+
(new DeadCodeElimination()).run(this);
590+
(new OptimizeDynScopesPass()).run(this);
601591
}
602592
}
603593

604-
/** Run any necessary passes to get the IR ready for interpretation */
605-
public synchronized Instr[] prepareForInterpretation(boolean isLambda) {
594+
public void initScope(boolean isLambda) {
595+
// Reset linearization, if any exists
596+
resetLinearizationData();
597+
606598
// Build CFG and run compiler passes, if necessary
607599
if (getCFG() == null) {
608600
buildCFG();
@@ -623,22 +615,35 @@ public synchronized Instr[] prepareForInterpretation(boolean isLambda) {
623615
// Run DCE and var load/store passes where applicable
624616
// But, if we have been passed in a list of passes to run
625617
// on the commandline, skip this opt.
626-
runDeadCodeAndVarLoadStorePasses();
618+
optimizeSimpleScopes();
627619
}
620+
}
621+
622+
/** Run any necessary passes to get the IR ready for interpretation */
623+
public synchronized Instr[] prepareForInterpretation(boolean isLambda) {
624+
initScope(isLambda);
628625

629626
checkRelinearization();
630627

631628
if (linearizedInstrArray != null) return linearizedInstrArray;
632629

630+
// System.out.println("-- passes run for: " + this + " = " + java.util.Arrays.toString(executedPasses.toArray()));
631+
633632
// Linearize CFG, etc.
634633
return prepareInstructions();
635634
}
636635

637636
/* SSS FIXME: Do we need to synchronize on this? Cache this info in a scope field? */
638637
/** Run any necessary passes to get the IR ready for compilation */
639638
public synchronized List<BasicBlock> prepareForCompilation() {
640-
// Reset linearization, since we will add JIT-specific flow and instrs
641-
resetLinearizationData();
639+
// For lambdas, we need to add a global ensure block to catch
640+
// uncaught breaks and throw a LocalJumpError.
641+
//
642+
// Since we dont re-JIT a previously JIT-ted closure,
643+
// mark all closures lambdas always. But, check if there are
644+
// other smarts available to us and eliminate adding
645+
// this code to every closure there is.
646+
initScope(this instanceof IRClosure);
642647

643648
runCompilerPasses(getManager().getJITPasses(this));
644649

@@ -1136,10 +1141,6 @@ public void inlineMethod(IRScope method, RubyModule implClass, int classToken, B
11361141
}
11371142
}
11381143

1139-
public void resetCFG() {
1140-
cfg = null;
1141-
}
1142-
11431144
/* Record a begin block -- not all scope implementations can handle them */
11441145
public void recordBeginBlock(IRClosure beginBlockClosure) {
11451146
throw new RuntimeException("BEGIN blocks cannot be added to: " + this.getClass().getName());

core/src/main/java/org/jruby/ir/instructions/CallBase.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,23 @@ public boolean computeScopeFlags(IRScope scope) {
164164
}
165165
}
166166

167+
// Kernel.local_variables inspects variables.
168+
// and JRuby implementation uses dyn-scope to access the static-scope
169+
// to output the local variables => we cannot strip dynscope in those cases.
170+
// FIXME: We need to decouple static-scope and dyn-scope.
171+
String mname = getMethodAddr().getName();
172+
if (mname.equals("local_variables")) {
173+
scope.getFlags().add(REQUIRES_DYNSCOPE);
174+
} else if (mname.equals("send") || mname.equals("__send__")) {
175+
Operand[] args = getCallArgs();
176+
if (args.length >= 1) {
177+
Operand meth = args[0];
178+
if (meth instanceof StringLiteral && "local_variables".equals(((StringLiteral)meth).string)) {
179+
scope.getFlags().add(REQUIRES_DYNSCOPE);
180+
}
181+
}
182+
}
183+
167184
return modifiedScope;
168185
}
169186
/**

core/src/main/java/org/jruby/ir/passes/AddCallProtocolInstructions.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -161,11 +161,6 @@ public Object execute(IRScope scope, Object... data) {
161161
return null;
162162
}
163163

164-
@Override
165-
public Object previouslyRun(IRScope scope) {
166-
return scope.hasExplicitCallProtocol() ? new Object() : null;
167-
}
168-
169164
@Override
170165
public void invalidate(IRScope scope) {
171166
// Cannot add call protocol instructions after we've added them once.
Lines changed: 7 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
11
package org.jruby.ir.passes;
22

33
import org.jruby.ir.IRClosure;
4-
import org.jruby.ir.IRScope;
54
import org.jruby.ir.IRFlags;
5+
import org.jruby.ir.IRScope;
66
import org.jruby.ir.dataflow.analyses.LiveVariablesProblem;
77
import org.jruby.ir.dataflow.analyses.LoadLocalVarPlacementProblem;
88
import org.jruby.ir.dataflow.analyses.StoreLocalVarPlacementProblem;
99
import org.jruby.ir.instructions.Instr;
10-
import org.jruby.ir.instructions.ResultInstr;
11-
import org.jruby.ir.instructions.LoadLocalVarInstr;
12-
import org.jruby.ir.instructions.StoreLocalVarInstr;
13-
import org.jruby.ir.operands.LocalVariable;
1410
import org.jruby.ir.operands.Operand;
1511
import org.jruby.ir.operands.Variable;
1612
import org.jruby.ir.representations.BasicBlock;
@@ -33,88 +29,15 @@ public List<Class<? extends CompilerPass>> getDependencies() {
3329
return DEPENDENCIES;
3430
}
3531

36-
private void setupLocalVarReplacement(LocalVariable v, IRScope s, Map<Operand, Operand> varRenameMap) {
37-
if (varRenameMap.get(v) == null) varRenameMap.put(v, s.getNewTemporaryVariableFor(v));
38-
}
39-
40-
private void decrementScopeDepth(LocalVariable v, IRScope s, Map<Operand, Operand> varRenameMap) {
41-
if (varRenameMap.get(v) == null) varRenameMap.put(v, v.cloneForDepth(v.getScopeDepth()-1));
42-
}
43-
44-
public void eliminateLocalVars(IRScope s) {
45-
Map<Operand, Operand> varRenameMap = new HashMap<Operand, Operand>();
46-
// Record the fact that we eliminated the scope
47-
s.getFlags().add(IRFlags.DYNSCOPE_ELIMINATED);
48-
49-
// Since the scope does not require a binding, no need to do
50-
// any analysis. It is sufficient to rename all local var uses
51-
// with a temporary variable.
52-
boolean parentScopeNeeded = false;
53-
for (BasicBlock b: s.getCFG().getBasicBlocks()) {
54-
for (Instr i: b.getInstrs()) {
55-
if (i instanceof ResultInstr) {
56-
Variable v = ((ResultInstr) i).getResult();
57-
// %self is local to every scope and never crosses scope boundaries and need not be spilled/refilled
58-
if (v instanceof LocalVariable && !v.isSelf()) {
59-
LocalVariable lv = (LocalVariable)v;
60-
if (lv.getScopeDepth() == 0) {
61-
// Make sure there is a replacement tmp-var allocated for lv
62-
setupLocalVarReplacement(lv, s, varRenameMap);
63-
} else {
64-
parentScopeNeeded = true;
65-
decrementScopeDepth(lv, s, varRenameMap);
66-
}
67-
}
68-
}
69-
70-
for (Variable v : i.getUsedVariables()) {
71-
if (v instanceof LocalVariable && !v.isSelf()) {
72-
LocalVariable lv = (LocalVariable)v;
73-
if (lv.getScopeDepth() == 0) {
74-
// Make sure there is a replacement tmp-var allocated for lv
75-
setupLocalVarReplacement(lv, s, varRenameMap);
76-
} else {
77-
// SSS FIXME: Ugly/Dirty! Some abstraction is broken.
78-
if (i instanceof LoadLocalVarInstr) {
79-
LoadLocalVarInstr llvi = (LoadLocalVarInstr)i;
80-
if (llvi.getLocalVar() == lv) {
81-
llvi.decrementLVarScopeDepth();
82-
}
83-
} else if (i instanceof StoreLocalVarInstr) {
84-
StoreLocalVarInstr slvi = (StoreLocalVarInstr)i;
85-
if (slvi.getLocalVar() == lv) {
86-
slvi.decrementLVarScopeDepth();
87-
}
88-
}
89-
90-
parentScopeNeeded = true;
91-
decrementScopeDepth(lv, s, varRenameMap);
92-
}
93-
}
94-
}
95-
}
96-
}
97-
98-
if (parentScopeNeeded) {
99-
s.getFlags().add(IRFlags.REUSE_PARENT_DYNSCOPE);
100-
}
101-
102-
// Rename all local var uses with their tmp-var stand-ins
103-
for (BasicBlock b: s.getCFG().getBasicBlocks()) {
104-
for (Instr i: b.getInstrs()) i.renameVars(varRenameMap);
105-
}
106-
}
107-
10832
@Override
10933
public Object execute(IRScope s, Object... data) {
11034
StoreLocalVarPlacementProblem slvp = new StoreLocalVarPlacementProblem();
11135

112-
// Make sure flags are computed
113-
s.computeScopeFlags();
36+
// No need to run this pass if we eliminated the dynamic scope!
37+
if (!s.getFlags().contains(IRFlags.DYNSCOPE_ELIMINATED) || s.getFlags().contains(IRFlags.REUSE_PARENT_DYNSCOPE)) {
38+
// Make sure flags are computed
39+
s.computeScopeFlags();
11440

115-
if (!s.getFlags().contains(IRFlags.REQUIRES_DYNSCOPE) && (data.length == 0 || data[0] != Boolean.TRUE)) {
116-
eliminateLocalVars(s);
117-
} else {
11841
Map<Operand, Operand> varRenameMap = new HashMap<Operand, Operand>();
11942
// 1. Figure out required stores
12043
// 2. Add stores
@@ -145,7 +68,7 @@ public Object execute(IRScope s, Object... data) {
14568
//
14669
// In the current implementation, nested scopes are processed independently (unlike Live Variable Analysis)
14770
// However, since this pass requires LVA information, in reality, we cannot run
148-
for (IRClosure c: s.getClosures()) execute(c, Boolean.TRUE);
71+
for (IRClosure c: s.getClosures()) execute(c);
14972
}
15073

15174
s.setDataFlowSolution(StoreLocalVarPlacementProblem.NAME, slvp);
@@ -163,6 +86,7 @@ public Object previouslyRun(IRScope scope) {
16386

16487
@Override
16588
public void invalidate(IRScope scope) {
89+
super.invalidate(scope);
16690
scope.setDataFlowSolution(StoreLocalVarPlacementProblem.NAME, null);
16791
}
16892
}

core/src/main/java/org/jruby/ir/passes/CFGBuilder.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@
22

33
import org.jruby.ir.IRScope;
44

5+
/**
6+
* CFGBuilder is mainly a pass to be lazy. We do not want to build CFG for scopes which are never called.
7+
*
8+
* Once we have a CFG that is the base data structure where we interact with instructions. The original
9+
* list of instructions from IRBuilder is no longer important. This is also why this pass is incapable
10+
* of invalidating the CFG.
11+
*/
12+
13+
514
public class CFGBuilder extends CompilerPass {
615
@Override
716
public String getLabel() {
@@ -20,6 +29,6 @@ public Object execute(IRScope scope, Object... data) {
2029

2130
@Override
2231
public void invalidate(IRScope scope) {
23-
scope.resetCFG();
32+
// CFG is primal information to a scope and cannot be recreated once generated.
2433
}
2534
}

0 commit comments

Comments
 (0)