Skip to content

Commit

Permalink
First commit of moving IR persistence toward being instance based so …
Browse files Browse the repository at this point in the history
…we can override it
enebo committed May 3, 2016
1 parent a633c63 commit 1d74956
Showing 10 changed files with 279 additions and 52 deletions.
6 changes: 6 additions & 0 deletions core/src/main/java/org/jruby/ir/IREvalScript.java
Original file line number Diff line number Diff line change
@@ -12,6 +12,7 @@
import org.jruby.ir.operands.LocalVariable;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.Signature;

public class IREvalScript extends IRClosure {
private List<IRClosure> beginBlocks;
@@ -71,6 +72,11 @@ public boolean isModuleOrInstanceEval() {
return evalType == EvalType.MODULE_EVAL || evalType == EvalType.INSTANCE_EVAL;
}

@Override
public Signature getSignature() {
return Signature.OPTIONAL;
}

/* Record a begin block -- not all scope implementations can handle them */
@Override
public void recordBeginBlock(IRClosure beginBlockClosure) {
4 changes: 4 additions & 0 deletions core/src/main/java/org/jruby/ir/IRScope.java
Original file line number Diff line number Diff line change
@@ -421,6 +421,10 @@ public void putLiveVariablesProblem(LiveVariablesProblem problem) {
if (problem != null) throw new IllegalStateException("LVP being stored when no FIC");
return;
}
if ("foo".equals(name) && problem != null) {
System.out.println("CFG: " + getCFG().toStringInstrs());
System.out.println("PROB: " + problem);
}
fullInterpreterContext.getDataFlowProblems().put(LiveVariablesProblem.NAME, problem);
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package org.jruby.ir.dataflow.analyses;

import java.util.BitSet;
import java.util.HashSet;
import org.jruby.dirgra.Edge;
import org.jruby.ir.dataflow.FlowGraphNode;
import org.jruby.ir.instructions.Instr;
import org.jruby.ir.instructions.ResultInstr;
import org.jruby.ir.operands.Variable;
import org.jruby.ir.representations.BasicBlock;

/**
*/
public class AddMissingAssignsNode extends FlowGraphNode<AddMissingAssignsProblem, AddMissingAssignsNode> {
private HashSet out;
private HashSet use;
private HashSet def;

public AddMissingAssignsNode(AddMissingAssignsProblem problem, BasicBlock bb) {
super(problem, bb);

out = new HashSet();
use = new HashSet();
def = new HashSet();
}

@Override
public void buildDataFlowVars(Instr i) {
if (i instanceof ResultInstr) registerVariable(((ResultInstr) i).getResult());

for (Variable x: i.getUsedVariables()) {
registerVariable(x);
}
}

@Override
public void applyPreMeetHandler() {
}

@Override
public void compute_MEET(Edge e, AddMissingAssignsNode pred) {
use.retainAll(pred.out);
}

@Override
public void applyTransferFunction(Instr instr) {
if (instr instanceof ResultInstr) def.add(((ResultInstr) instr).getResult());

for (Variable variable: instr.getUsedVariables()) {
use.add(variable);
}
}

@Override
public void finalizeSolution() {
out = def;
}

@Override
public boolean solutionChanged() {
return !out.equals(def);
}

private void registerVariable(Variable variable) {
if (!problem.hasVariable(variable)) problem.addVariable(variable);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package org.jruby.ir.dataflow.analyses;

import java.util.HashSet;
import java.util.Set;
import org.jruby.ir.IRScope;
import org.jruby.ir.dataflow.DataFlowProblem;
import org.jruby.ir.operands.Variable;
import org.jruby.ir.representations.BasicBlock;

/**
*/
public class AddMissingAssignsProblem extends DataFlowProblem<AddMissingAssignsProblem, AddMissingAssignsNode> {
public static final String NAME = "Live Variables Analysis";

private Set<Variable> variables;

public AddMissingAssignsProblem(IRScope scope) {
super(DataFlowProblem.DF_Direction.FORWARD);
setup(scope);
variables = new HashSet<>();
}

protected void addVariable(Variable variable) {
variables.add(variable);
}

protected boolean hasVariable(Variable variable) {
return variables.contains(variable);
}

@Override
public AddMissingAssignsNode buildFlowGraphNode(BasicBlock bb) {
return new AddMissingAssignsNode(this, bb);
}

@Override
public String getName() {
return NAME;
}
}
54 changes: 38 additions & 16 deletions core/src/main/java/org/jruby/ir/persistence/IRReader.java
Original file line number Diff line number Diff line change
@@ -29,25 +29,47 @@
* @author enebo
*/
public class IRReader implements IRPersistenceValues {
protected IRManager manager;
protected IRReaderDecoder decoder;

@Deprecated
public IRReader() {
// This is so anyone who might have extended this pre-OOd will not fail to compile.
throw new RuntimeException("Old format was all static so this noarg constructor should not get called.");
}

public IRReader(IRManager manager, final IRReaderDecoder file) {
this.manager = manager;
this.decoder = file;
}

public static IRScope load(IRManager manager, final IRReaderDecoder file) throws IOException {
int version = file.decodeIntRaw();
return new IRReader(manager, file).load();
}

protected void loadPrelude() throws IOException {
int version = decoder.decodeIntRaw();

if (version != VERSION) {
throw new IOException("Trying to read incompatable persistence format (version found: " +
version + ", version expected: " + VERSION);
}
int headersOffset = file.decodeIntRaw();
int headersOffset = decoder.decodeIntRaw();
if (RubyInstanceConfig.IR_READING_DEBUG) System.out.println("header_offset = " + headersOffset);
int poolOffset = file.decodeIntRaw();
int poolOffset = decoder.decodeIntRaw();
if (RubyInstanceConfig.IR_READING_DEBUG) System.out.println("pool_offset = " + headersOffset);
decoder.seek(headersOffset);
}

protected IRScope load() throws IOException {
loadPrelude();

file.seek(headersOffset);
int scopesToRead = file.decodeInt();
int scopesToRead = decoder.decodeInt();
if (RubyInstanceConfig.IR_READING_DEBUG) System.out.println("scopes to read = " + scopesToRead);

KeyValuePair<IRScope, Integer>[] scopes = new KeyValuePair[scopesToRead];
for (int i = 0; i < scopesToRead; i++) {
scopes[i] = decodeScopeHeader(manager, file);
scopes[i] = decodeScopeHeader();
}

// Lifecycle woes. All IRScopes need to exist before we can decodeInstrs.
@@ -60,7 +82,7 @@ public static IRScope load(IRManager manager, final IRReaderDecoder file) throws
@Override
public List<Instr> call() throws Exception {
// System.out.println("eager");
return file.decodeInstructionsAt(scope, instructionsOffset);
return decoder.decodeInstructionsAt(scope, instructionsOffset);
}
});
}
@@ -75,7 +97,7 @@ public List<Instr> call() throws Exception {
return scopes[0].getKey(); // topmost scope;
}

private static KeyValuePair<IRScope, Integer> decodeScopeHeader(IRManager manager, IRReaderDecoder decoder) {
private KeyValuePair<IRScope, Integer> decodeScopeHeader() {
if (RubyInstanceConfig.IR_READING_DEBUG) System.out.println("DECODING SCOPE HEADER");
IRScopeType type = decoder.decodeIRScopeType();
if (RubyInstanceConfig.IR_READING_DEBUG) System.out.println("IRScopeType = " + type);
@@ -85,7 +107,7 @@ private static KeyValuePair<IRScope, Integer> decodeScopeHeader(IRManager manage
if (RubyInstanceConfig.IR_READING_DEBUG) System.out.println("LINE = " + line);
int tempVarsCount = decoder.decodeInt();
if (RubyInstanceConfig.IR_READING_DEBUG) System.out.println("# of Temp Vars = " + tempVarsCount);
Map<String, Integer> indices = decodeScopeLabelIndices(decoder);
Map<String, Integer> indices = decodeScopeLabelIndices();

IRScope parent = type != IRScopeType.SCRIPT_BODY ? decoder.decodeScope() : null;
Signature signature;
@@ -98,16 +120,16 @@ private static KeyValuePair<IRScope, Integer> decodeScopeHeader(IRManager manage
StaticScope parentScope = parent == null ? null : parent.getStaticScope();
// FIXME: It seems wrong we have static scope + local vars both being persisted. They must have the same values
// and offsets?
StaticScope staticScope = decodeStaticScope(decoder, parentScope);
IRScope scope = createScope(manager, type, name, line, parent, signature, staticScope);
StaticScope staticScope = decodeStaticScope(parentScope);
IRScope scope = createScope(type, name, line, parent, signature, staticScope);

scope.setTemporaryVariableCount(tempVarsCount);
// FIXME: Replace since we are defining this...perhaps even make a persistence constructor
scope.setLabelIndices(indices);

// FIXME: This is odd, but ClosureLocalVariable wants it's defining closure...feels wrong.
// But because of this we have to push decoding lvars to the end of the scope info.
scope.setLocalVariables(decodeScopeLocalVariables(decoder, scope));
scope.setLocalVariables(decodeScopeLocalVariables(scope));

decoder.addScope(scope);

@@ -116,7 +138,7 @@ private static KeyValuePair<IRScope, Integer> decodeScopeHeader(IRManager manage
return new KeyValuePair<>(scope, instructionsOffset);
}

private static Map<String, LocalVariable> decodeScopeLocalVariables(IRReaderDecoder decoder, IRScope scope) {
private Map<String, LocalVariable> decodeScopeLocalVariables(IRScope scope) {
int size = decoder.decodeInt();
Map<String, LocalVariable> localVariables = new HashMap(size);
for (int i = 0; i < size; i++) {
@@ -131,7 +153,7 @@ private static Map<String, LocalVariable> decodeScopeLocalVariables(IRReaderDeco
return localVariables;
}

private static Map<String, Integer> decodeScopeLabelIndices(IRReaderDecoder decoder) {
private Map<String, Integer> decodeScopeLabelIndices() {
int labelIndicesSize = decoder.decodeInt();
Map<String, Integer> indices = new HashMap<String, Integer>(labelIndicesSize);
for (int i = 0; i < labelIndicesSize; i++) {
@@ -140,15 +162,15 @@ private static Map<String, Integer> decodeScopeLabelIndices(IRReaderDecoder deco
return indices;
}

private static StaticScope decodeStaticScope(IRReaderDecoder decoder, StaticScope parentScope) {
private StaticScope decodeStaticScope(StaticScope parentScope) {
StaticScope scope = StaticScopeFactory.newStaticScope(parentScope, decoder.decodeStaticScopeType(), decoder.decodeStringArray());

scope.setSignature(decoder.decodeSignature());

return scope;
}

public static IRScope createScope(IRManager manager, IRScopeType type, String name, int line,
public IRScope createScope(IRScopeType type, String name, int line,
IRScope lexicalParent, Signature signature, StaticScope staticScope) {

switch (type) {
112 changes: 76 additions & 36 deletions core/src/main/java/org/jruby/ir/persistence/IRWriter.java
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@
import org.jruby.ir.IRClosure;
import org.jruby.ir.IRMethod;
import org.jruby.ir.IRScope;
import org.jruby.ir.IRScopeType;
import org.jruby.ir.IRScriptBody;
import org.jruby.ir.instructions.Instr;
import org.jruby.ir.operands.LocalVariable;
@@ -12,6 +13,7 @@
import java.io.IOException;
import java.util.List;
import java.util.Map;
import org.jruby.runtime.Signature;

/**
* Write IR data out to persistent store. IRReader is capable of re-reading this
@@ -20,29 +22,45 @@
* information.
*/
public class IRWriter {
public static void persist(IRWriterEncoder file, IRScope script) throws IOException {
file.startEncoding(script);
persistScopeInstructions(file, script); // recursive dump of all scopes instructions
protected IRWriterEncoder file;

file.startEncodingScopeHeaders(script);
persistScopeHeaders(file, script); // recursive dump of all defined scope headers
file.endEncodingScopeHeaders(script);
@Deprecated
public IRWriter() {
// This is so anyone who might have extended this pre-OOd will not fail to compile.
throw new RuntimeException("Old format was all static so this noarg constructor should not get called.");
}

file.endEncoding(script);
public IRWriter(IRWriterEncoder file) {
this.file = file;
}

private static void persistScopeInstructions(IRWriterEncoder file, IRScope parent) {
persistScopeInstrs(file, parent);
public void persist(IRScope scope) throws IOException {
startEncodingScope(scope);

startInstructionsSection(scope);
persistScopeInstructions(scope); // recursive dump of all scopes instructions
endInstructionsSection(scope);

startScopeHeadersSection(scope);
persistScopeHeaders(scope, true); // recursive dump of all defined scope headers
endScopeHeadersSection(scope);

endEncodingScope(scope);
}

protected void persistScopeInstructions(IRScope parent) {
persistScopeInstrs(parent);

for (IRScope scope: parent.getLexicalScopes()) {
persistScopeInstructions(file, scope);
persistScopeInstructions(scope);
}
}

// {operation, [operands]}*
private static void persistScopeInstrs(IRWriterEncoder file, IRScope scope) {
protected void persistScopeInstrs(IRScope scope) {
file.startEncodingScopeInstrs(scope);

// FIXME IRScope should provide a guaranteed IC which will build if lazy or return what it has.
// Currently methods are only lazy scopes so we need to build them if we decide to persist them.
if (scope instanceof IRMethod && !scope.hasBeenBuilt()) {
((IRMethod) scope).lazilyAcquireInterpreterContext();
@@ -57,19 +75,19 @@ private static void persistScopeInstrs(IRWriterEncoder file, IRScope scope) {

// recursive dump of all scopes. Each scope records offset into persisted file where there
// instructions reside. That is extra logic here in currentInstrIndex + instrsLocations
private static void persistScopeHeaders(IRWriterEncoder file, IRScope parent) {
persistScopeHeader(file, parent);
protected void persistScopeHeaders(IRScope parent, boolean topScope) {
persistScopeHeader(parent, topScope, false);

for (IRScope scope: parent.getLexicalScopes()) {
persistScopeHeaders(file, scope);
persistScopeHeaders(scope, false);
}
}

// script body: {type,name,linenumber,{static_scope},instrs_offset}
// closure scopes: {type,name,linenumber,lexical_parent_name, lexical_parent_line,is_for,arity,arg_type,{static_scope},instrs_offset}
// other scopes: {type,name,linenumber,lexical_parent_name, lexical_parent_line,{static_scope}, instrs_offset}
// for non-for scopes is_for,arity, and arg_type will be 0.
private static void persistScopeHeader(IRWriterEncoder file, IRScope scope) {
protected void persistScopeHeader(IRScope scope, boolean topScope, boolean doNotRecordInstrOffset) {
if (RubyInstanceConfig.IR_WRITING_DEBUG) System.out.println("Writing Scope Header");
file.startEncodingScopeHeader(scope);
if (RubyInstanceConfig.IR_WRITING_DEBUG) System.out.println("IRScopeType = " + scope.getScopeType());
@@ -81,32 +99,34 @@ private static void persistScopeHeader(IRWriterEncoder file, IRScope scope) {
if (RubyInstanceConfig.IR_WRITING_DEBUG) System.out.println("# of temp vars = " + scope.getTemporaryVariablesCount());
file.encode(scope.getTemporaryVariablesCount());

persistScopeLabelIndices(scope, file);
persistScopeLabelIndices(scope);

if (!(scope instanceof IRScriptBody)) file.encode(scope.getLexicalParent());
// topScope is for persisting closures directly. In this case we are not interested in its parent.
if (!(scope instanceof IRScriptBody) && !topScope) {
file.encode(scope.getLexicalParent());
}

if (scope instanceof IRClosure) {
IRClosure closure = (IRClosure) scope;
if (scope instanceof IRClosure) file.encode(((IRClosure) scope).getSignature().encode());

file.encode(closure.getSignature().encode());
file.encode(scope.getStaticScope());
persistLocalVariables(scope);
if (doNotRecordInstrOffset) {
file.encode(0);
} else {
file.endEncodingScopeHeader(scope);
}

persistStaticScope(file, scope.getStaticScope());
persistLocalVariables(scope, file);
file.endEncodingScopeHeader(scope);
}

// FIXME: I hacked around our lvar types for now but this hsould be done in a less ad-hoc fashion.
private static void persistLocalVariables(IRScope scope, IRWriterEncoder file) {
protected void persistLocalVariables(IRScope scope) {
Map<String, LocalVariable> localVariables = scope.getLocalVariables();
file.encode(localVariables.size());
for (String name: localVariables.keySet()) {
file.encode(name);
file.encode(localVariables.get(name).getOffset()); // No need to write depth..it is zero.
int numberOfVariables = localVariables.size();
file.encode(numberOfVariables);
for (int i = 0; i < numberOfVariables; i++) {
file.encode(localVariables.get(i));
}
}

private static void persistScopeLabelIndices(IRScope scope, IRWriterEncoder file) {
protected void persistScopeLabelIndices(IRScope scope) {
Map<String,Integer> labelIndices = scope.getVarIndices();
if (RubyInstanceConfig.IR_WRITING_DEBUG) System.out.println("LABEL_SIZE: " + labelIndices.size());
file.encode(labelIndices.size());
@@ -119,10 +139,30 @@ private static void persistScopeLabelIndices(IRScope scope, IRWriterEncoder file
if (RubyInstanceConfig.IR_WRITING_DEBUG) System.out.println("DONE LABELS: " + labelIndices.size());
}

// {type,[variables],signature}
private static void persistStaticScope(IRWriterEncoder file, StaticScope staticScope) {
file.encode(staticScope.getType());
file.encode(staticScope.getVariables());
file.encode(staticScope.getSignature());
protected void startEncodingScope(IRScope scope) {
file.startEncoding(scope);
}

protected void endEncodingScope(IRScope scope) {
file.endEncoding(scope);
}

protected void startInstructionsSection(IRScope scope) {
}

protected void endInstructionsSection(IRScope scope) {
}

protected void startScopeHeadersSection(IRScope scope) {
file.startEncodingScopeHeaders(scope);
}

protected void endScopeHeadersSection(IRScope scope) {
file.endEncodingScopeHeaders(scope);
}

@Deprecated
public static void persist(IRWriterEncoder file, IRScope script) throws IOException {
new IRWriter(file).persist(script);
}
}
Original file line number Diff line number Diff line change
@@ -11,6 +11,7 @@
import org.jruby.ir.IRScopeType;
import org.jruby.ir.Operation;
import org.jruby.ir.instructions.Instr;
import org.jruby.ir.operands.LocalVariable;
import org.jruby.ir.operands.Operand;
import org.jruby.ir.operands.OperandType;
import org.jruby.parser.StaticScope;
@@ -73,6 +74,10 @@ public void encode(IRScopeType value) {
public void encode(StaticScope.Type value) {
}

@Override
public void encode(LocalVariable value) {
}

@Override
public void encode(Operation value) {
}
@@ -125,6 +130,10 @@ public void encode(Signature value) {
public void encode(RubyEvent event) {
}

@Override
public void encode(StaticScope scope) {
}

@Override
public void startEncodingScopeHeader(IRScope scope) {
}
Original file line number Diff line number Diff line change
@@ -5,8 +5,10 @@
import org.jruby.ir.IRScopeType;
import org.jruby.ir.Operation;
import org.jruby.ir.instructions.Instr;
import org.jruby.ir.operands.LocalVariable;
import org.jruby.ir.operands.Operand;
import org.jruby.ir.operands.OperandType;
import org.jruby.parser.StaticScope;
import org.jruby.parser.StaticScope.Type;
import org.jruby.runtime.RubyEvent;
import org.jruby.runtime.Signature;
@@ -27,8 +29,10 @@ public interface IRWriterEncoder {
public void encode(Instr value);
public void encode(IRScope scope);
public void encode(IRScopeType value);
public void encode(LocalVariable value);
public void encode(Signature signature);
public void encode(RubyEvent event);
public void encode(StaticScope staticScope);
public void encode(Type value);
public void encode(Operation value);
public void encode(Operand value);
15 changes: 15 additions & 0 deletions core/src/main/java/org/jruby/ir/persistence/IRWriterStream.java
Original file line number Diff line number Diff line change
@@ -11,6 +11,7 @@
import org.jruby.ir.IRScopeType;
import org.jruby.ir.Operation;
import org.jruby.ir.instructions.Instr;
import org.jruby.ir.operands.LocalVariable;
import org.jruby.ir.operands.Operand;
import org.jruby.ir.operands.OperandType;
import org.jruby.parser.StaticScope;
@@ -162,6 +163,12 @@ public void encode(String[] values) {
}
}

@Override
public void encode(LocalVariable variable) {
encode(variable.getName());
encode(variable.getOffset()); // No need to write depth..it is zero.
}

@Override
public void encode(Operand operand) {
operand.encode(this);
@@ -202,6 +209,14 @@ public void encode(RubyEvent event) {
encode((byte) event.ordinal());
}

@Override
public void encode(StaticScope staticScope) {
encode(staticScope.getType());
encode(staticScope.getVariables());
encode(staticScope.getSignature());
}


@Override
public void encode(StaticScope.Type value) {
encode((byte) value.ordinal());
20 changes: 20 additions & 0 deletions core/src/main/java/org/jruby/ir/representations/CFG.java
Original file line number Diff line number Diff line change
@@ -305,6 +305,26 @@ public DirectedGraph<BasicBlock> build(Instr[] instrs) {
}
}

// Insert starts with false edges for each exception region so we can account
// for begin; a = 1; rescue; p a; end and that Java is scoped differently.
for (ExceptionRegion rr: allExceptionRegions) {
BasicBlock newStart = createBB(nestedExceptionRegions);
BasicBlock oldStart = rr.getStartBB();

// move all incoming edges from oldStart to our new one
for (Edge<BasicBlock> edge: graph.findOrCreateVertexFor(oldStart).getIncomingEdges()) {
graph.removeEdge(edge);
graph.addEdge(edge.getSource().getData(), newStart, edge.getType());
}

addEdge(newStart, oldStart, EdgeType.FALL_THROUGH);

Label rescueLabel = rr.getFirstRescueBlockLabel();
if (!Label.UNRESCUED_REGION_LABEL.equals(rescueLabel)) {
addEdge(newStart, bbMap.get(rr.getFirstRescueBlockLabel()), EdgeType.EXCEPTION);
}
}

// Process all rescued regions
for (ExceptionRegion rr: allExceptionRegions) {
// When this exception region represents an unrescued region

0 comments on commit 1d74956

Please sign in to comment.