Skip to content

Commit

Permalink
Showing 35 changed files with 484 additions and 190 deletions.
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@
import org.jruby.RubyClass;
import org.jruby.RubyModule;
import org.jruby.ir.*;
import org.jruby.ir.operands.InterpreterContext;
import org.jruby.ir.representations.CFG;
import org.jruby.ir.interpreter.Interpreter;
import org.jruby.ir.runtime.IRRuntimeHelpers;
@@ -139,8 +140,8 @@ public void ensureInstrsReady() {
// SSS FIXME: Move this out of here to some other place?
// Prepare method if not yet done so we know if the method has an explicit/implicit call protocol
if (method.getInstrsForInterpretation() == null) {
method.prepareForInterpretation(false);
this.pushScope = !method.getFlags().contains(IRFlags.DYNSCOPE_ELIMINATED);
InterpreterContext context = method.prepareForInterpretation();
this.pushScope = !context.getFlags().contains(IRFlags.DYNSCOPE_ELIMINATED);
}
}

44 changes: 29 additions & 15 deletions core/src/main/java/org/jruby/ir/IRBuilder.java
Original file line number Diff line number Diff line change
@@ -212,6 +212,10 @@ public void addInstr(Instr i) {
instrs.add(i);
}

public void addInstrAtBeginning(Instr i) {
instrs.add(0, i);
}

public void emitBody(IRBuilder b, IRScope s) {
b.addInstr(s, new LabelInstr(start));
for (Instr i: instrs) {
@@ -293,6 +297,16 @@ public void addInstr(IRScope s, Instr i) {
}
}

public void addInstrAtBeginning(IRScope s, Instr i) {
// If we are building an ensure body, stash the instruction
// in the ensure body's list. If not, add it to the scope directly.
if (ensureBodyBuildStack.empty()) {
s.addInstrAtBeginning(i);
} else {
ensureBodyBuildStack.peek().addInstrAtBeginning(i);
}
}

private Operand getImplicitBlockArg(IRScope s) {
int n = 0;
while (s != null && s instanceof IRClosure) {
@@ -505,8 +519,7 @@ public Operand buildLambda(LambdaNode node, IRScope s) {
// can be U_NIL if the node is an if node with returns in both branches.
if (closureRetVal != U_NIL) closureBuilder.addInstr(closure, new ReturnInstr(closureRetVal));

// Added as part of 'prepareForInterpretation' code.
// handleBreakAndReturnsInLambdas(closure);
handleBreakAndReturnsInLambdas(closure);

Variable lambda = s.createTemporaryVariable();
// SSS FIXME: Is this the right self here?
@@ -891,8 +904,8 @@ private void handleNonlocalReturnInMethod(IRScope s) {
//
// Add label and marker instruction in reverse order to the beginning
// so that the label ends up being the first instr.
s.addInstrAtBeginning(new ExceptionRegionStartMarkerInstr(gebLabel));
s.addInstrAtBeginning(new LabelInstr(rBeginLabel));
addInstrAtBeginning(s, new ExceptionRegionStartMarkerInstr(gebLabel));
addInstrAtBeginning(s, new LabelInstr(rBeginLabel));
addInstr(s, new ExceptionRegionEndMarkerInstr());

// Receive exceptions (could be anything, but the handler only processes IRReturnJumps)
@@ -1970,19 +1983,13 @@ public void buildMultipleAsgn19Assignment(final MultipleAsgn19Node multipleAsgnN
}
}

/* ------------------------------------------------------------------
* This code is added on demand at runtime in the interpreter code.
* For JIT, this may have to be added always!
// These two methods could have been DRY-ed out if we had closures.
// For now, just duplicating code.
private void handleBreakAndReturnsInLambdas(IRClosure s) {
Label rBeginLabel = s.getNewLabel();
Label rEndLabel = s.getNewLabel();
Label rescueLabel = s.getNewLabel();
Label rescueLabel = Label.GLOBAL_ENSURE_BLOCK_LABEL;

// protect the entire body as it exists now with the global ensure block
s.addInstrAtBeginning(new ExceptionRegionStartMarkerInstr(rescueLabel));
addInstrAtBeginning(s, new ExceptionRegionStartMarkerInstr(rescueLabel));
addInstr(s, new ExceptionRegionEndMarkerInstr());

// Receive exceptions (could be anything, but the handler only processes IRBreakJumps)
@@ -1992,14 +1999,13 @@ private void handleBreakAndReturnsInLambdas(IRClosure s) {

// Handle break using runtime helper
// --> IRRuntimeHelpers.handleBreakAndReturnsInLambdas(context, scope, bj, blockType)
Variable ret = createTemporaryVariable();
addInstr(s, new RuntimeHelperCall(ret, "handleBreakAndReturnsInLambdas", new Operand[]{exc} ));
Variable ret = s.createTemporaryVariable();
addInstr(s, new RuntimeHelperCall(ret, RuntimeHelperCall.Methods.HANDLE_BREAK_AND_RETURNS_IN_LAMBDA, new Operand[]{exc} ));
addInstr(s, new ReturnInstr(ret));

// End
addInstr(s, new LabelInstr(rEndLabel));
}
* ------------------------------------------------------------------ */

public void receiveMethodArgs(final ArgsNode argsNode, IRScope s) {
receiveArgs(argsNode, s);
@@ -2524,6 +2530,14 @@ public Operand buildIter(final IterNode iterNode, IRScope s) {
closureBuilder.addInstr(closure, new ReturnInstr(closureRetVal));
}

// Always add break/return handling even though this
// is only required for lambdas, but we don't know at this time,
// if this is a lambda or not.
//
// SSS FIXME: At a later time, see if we can optimize this and
// do this on demand.
closureBuilder.handleBreakAndReturnsInLambdas(closure);

return new WrappedIRClosure(s.getSelf(), closure);
}

45 changes: 0 additions & 45 deletions core/src/main/java/org/jruby/ir/IRClosure.java
Original file line number Diff line number Diff line change
@@ -36,7 +36,6 @@ public class IRClosure extends IRScope {

private Arity arity;
private int argumentType;
public boolean addedGEBForUncaughtBreaks;

/** Added for interp/JIT purposes */
private BlockBody body;
@@ -78,7 +77,6 @@ protected IRClosure(IRClosure c, IRScope lexicalParent, int closureId, String fu
} else {
this.body = new InterpretedIRBlockBody(this, c.body.arity(), c.body.getArgumentType());
}
this.addedGEBForUncaughtBreaks = false;
this.blockArgs = new ArrayList<Operand>();
this.arity = c.arity;
}
@@ -292,7 +290,6 @@ protected IRClosure cloneForInlining(CloneInfo ii, IRClosure clone) {
// FIXME: This is fragile. Untangle this state.
// Why is this being copied over to InterpretedIRBlockBody?
clone.setParameterList(this.parameterList);
clone.addedGEBForUncaughtBreaks = this.addedGEBForUncaughtBreaks;
clone.isBeginEndBlock = this.isBeginEndBlock;

SimpleCloneInfo clonedII = ii.cloneForCloningClosure(clone);
@@ -327,48 +324,6 @@ public IRClosure cloneForInlining(CloneInfo ii) {
return cloneForInlining(ii, clonedClosure);
}

// Add a global-ensure-block to catch uncaught breaks
// This is usually required only if this closure is being
// used as a lambda, but it is safe to add this for any closure

protected boolean addGEBForUncaughtBreaks() {
// Nothing to do if already done
if (addedGEBForUncaughtBreaks) {
return false;
}

CFG cfg = cfg();
BasicBlock geb = cfg.getGlobalEnsureBB();
if (geb == null) {
geb = new BasicBlock(cfg, new Label("_GLOBAL_ENSURE_BLOCK", 0));
Variable exc = createTemporaryVariable();
geb.addInstr(new ReceiveJRubyExceptionInstr(exc)); // JRuby implementation exception
// Handle uncaught break and non-local returns using runtime helpers
Variable ret = createTemporaryVariable();
geb.addInstr(new RuntimeHelperCall(ret,
RuntimeHelperCall.Methods.HANDLE_BREAK_AND_RETURNS_IN_LAMBDA, new Operand[]{exc} ));
geb.addInstr(new ReturnInstr(ret));
cfg.addGlobalEnsureBB(geb);
} else {
// SSS FIXME: Assumptions:
//
// First instr is a 'ReceiveExceptionBase'
// Last instr is a 'ThrowExceptionInstr' -- replaced by handleBreakAndReturnsInLambdas

List<Instr> instrs = geb.getInstrs();
Variable exc = ((ReceiveExceptionBase)instrs.get(0)).getResult();
Variable ret = createTemporaryVariable();
instrs.set(instrs.size()-1, new RuntimeHelperCall(ret,
RuntimeHelperCall.Methods.HANDLE_BREAK_AND_RETURNS_IN_LAMBDA, new Operand[]{exc} ));
geb.addInstr(new ReturnInstr(ret));
}

// Update scope
addedGEBForUncaughtBreaks = true;

return true;
}

@Override
public void setName(String name) {
// We can distinguish closures only with parent scope name
57 changes: 18 additions & 39 deletions core/src/main/java/org/jruby/ir/IRScope.java
Original file line number Diff line number Diff line change
@@ -108,7 +108,8 @@ public abstract class IRScope implements ParseResult {
/** What passes have been run on this scope? */
private List<CompilerPass> executedPasses;

private Instr[] linearizedInstrArray;
/** What the interpreter depends on to interpret this IRScope */
private InterpreterContext interpreterContext;
private List<BasicBlock> linearizedBBList;
protected int temporaryVariableIndex;
protected int floatVariableIndex;
@@ -155,7 +156,7 @@ protected IRScope(IRScope s, IRScope lexicalParent) {
this.dfProbs = new HashMap<String, DataFlowProblem>();
this.nextVarIndex = new HashMap<String, Integer>(); // SSS FIXME: clone!
this.cfg = null;
this.linearizedInstrArray = null;
this.interpreterContext = null;
this.linearizedBBList = null;

this.flagsComputed = s.flagsComputed;
@@ -187,7 +188,7 @@ public IRScope(IRManager manager, IRScope lexicalParent, String name,
this.dfProbs = new HashMap<String, DataFlowProblem>();
this.nextVarIndex = new HashMap<String, Integer>();
this.cfg = null;
this.linearizedInstrArray = null;
this.interpreterContext = null;
this.linearizedBBList = null;
this.flagsComputed = false;
flags.remove(CAN_RECEIVE_BREAKS);
@@ -466,10 +467,10 @@ public CFG getCFG() {
return cfg;
}

private synchronized Instr[] prepareInstructions() {
private synchronized InterpreterContext prepareInstructions() {
checkRelinearization();

if (linearizedInstrArray != null) return linearizedInstrArray; // Already prepared
if (interpreterContext != null) return interpreterContext; // Already prepared

setupLinearization();

@@ -520,7 +521,7 @@ private synchronized Instr[] prepareInstructions() {
// System.out.println("SCOPE: " + getName());
// System.out.println("INSTRS: " + cfg().toStringInstrs());

linearizedInstrArray = newInstrs.toArray(new Instr[newInstrs.size()]);
Instr[] linearizedInstrArray = newInstrs.toArray(new Instr[newInstrs.size()]);

// Pass 2: Use ipc info from previous to mark all linearized instrs rpc
ipc = 0;
@@ -537,7 +538,10 @@ private synchronized Instr[] prepareInstructions() {
}
}

return linearizedInstrArray;
interpreterContext = new InterpreterContext(getTemporaryVariablesCount(), getBooleanVariablesCount(),
getFixnumVariablesCount(), getFloatVariablesCount(),getFlags().clone(), linearizedInstrArray);

return interpreterContext;
}

private boolean isUnsafeScope() {
@@ -605,7 +609,7 @@ private void optimizeSimpleScopes() {
}
}

private void initScope(boolean isLambda, boolean jitMode) {
private void initScope(boolean jitMode) {
// Reset linearization, if any exists
resetLinearizationData();

@@ -614,15 +618,6 @@ private void initScope(boolean isLambda, boolean jitMode) {
buildCFG();
}

if (isLambda) {
// Add a global ensure block to catch uncaught breaks
// and throw a LocalJumpError.
if (((IRClosure)this).addGEBForUncaughtBreaks()) {
resetState();
computeScopeFlags();
}
}

runCompilerPasses(getManager().getCompilerPasses(this));

if (!jitMode && RubyInstanceConfig.IR_COMPILER_PASSES == null) {
@@ -636,13 +631,11 @@ private void initScope(boolean isLambda, boolean jitMode) {
}

/** Run any necessary passes to get the IR ready for interpretation */
public synchronized Instr[] prepareForInterpretation(boolean isLambda) {
initScope(isLambda, false);
public synchronized InterpreterContext prepareForInterpretation() {
initScope(false);

checkRelinearization();

if (linearizedInstrArray != null) return linearizedInstrArray;

// System.out.println("-- passes run for: " + this + " = " + java.util.Arrays.toString(executedPasses.toArray()));

// Linearize CFG, etc.
@@ -652,14 +645,7 @@ public synchronized Instr[] prepareForInterpretation(boolean isLambda) {
/* SSS FIXME: Do we need to synchronize on this? Cache this info in a scope field? */
/** Run any necessary passes to get the IR ready for compilation */
public synchronized List<BasicBlock> prepareForCompilation() {
// For lambdas, we need to add a global ensure block to catch
// uncaught breaks and throw a LocalJumpError.
//
// Since we dont re-JIT a previously JIT-ted closure,
// mark all closures lambdas always. But, check if there are
// other smarts available to us and eliminate adding
// this code to every closure there is.
initScope(this instanceof IRClosure, true);
initScope(true);

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

@@ -1062,15 +1048,8 @@ public List<Instr> getInstrs() {
return instrList;
}

public Instr[] getInstrsForInterpretation() {
return linearizedInstrArray;
}

public Instr[] getInstrsForInterpretation(boolean isLambda) {
if (linearizedInstrArray == null) {
prepareForInterpretation(isLambda);
}
return linearizedInstrArray;
public InterpreterContext getInstrsForInterpretation() {
return interpreterContext;
}

public void resetLinearizationData() {
@@ -1113,7 +1092,7 @@ public CFG cfg() {

public void resetState() {
relinearizeCFG = true;
linearizedInstrArray = null;
interpreterContext = null;
cfg.resetState();

// reset flags
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/ir/IRScriptBody.java
Original file line number Diff line number Diff line change
@@ -80,7 +80,7 @@ public boolean isScriptScope() {
}

public IRubyObject interpret(ThreadContext context, IRubyObject self) {
prepareForInterpretation(false);
prepareForInterpretation();

String name = "(root)";
if (IRRuntimeHelpers.isDebug()) {
Original file line number Diff line number Diff line change
@@ -118,16 +118,21 @@ public void addStores(Map<Operand, Operand> varRenameMap) {

// Allocate global-ensure block, if necessary
if ((mightRequireGlobalEnsureBlock == true) && !dirtyVars.isEmpty()) {
Variable exc = cfgScope.createTemporaryVariable();
BasicBlock geb = new BasicBlock(cfg, new Label("_GLOBAL_ENSURE_BLOCK", 0));

ListIterator instrs = geb.getInstrs().listIterator();
ListIterator<Instr> instrs;
BasicBlock geb = cfg.getGlobalEnsureBB();
if (geb == null) {
Variable exc = cfgScope.createTemporaryVariable();
geb = new BasicBlock(cfg, Label.GLOBAL_ENSURE_BLOCK_LABEL);
geb.addInstr(new ReceiveJRubyExceptionInstr(exc)); // JRuby implementation exception handling
geb.addInstr(new ThrowExceptionInstr(exc));
cfg.addGlobalEnsureBB(geb);
}

instrs.add(new ReceiveJRubyExceptionInstr(exc)); // JRuby implementation exception handling
instrs = geb.getInstrs().listIterator(geb.getInstrs().size());
Instr i = instrs.previous();
// Assumption: Last instr should always be a control-transfer instruction
assert i.getOperation().transfersControl(): "Last instruction of GEB in scope: " + getScope() + " is " + i + ", not a control-xfer instruction";
addClosureExitStoreLocalVars(instrs, dirtyVars, varRenameMap);
instrs.add(new ThrowExceptionInstr(exc));

cfg.addGlobalEnsureBB(geb);
}
}
}
Original file line number Diff line number Diff line change
@@ -98,6 +98,6 @@ public void visit(IRVisitor visitor) {

@Override
public String toString() {
return result + "`" + (pieces == null ? "[]" : pieces) + "`";
return result + " = `" + (pieces == null ? "[]" : pieces) + "`";
}
}
Original file line number Diff line number Diff line change
@@ -44,6 +44,12 @@ public void updateResult(Variable v) {
this.result = v;
}

// we don't want to convert const_missing to an actual call
@Override
public CallBase specializeForInterpretation() {
return this;
}

@Override
public Instr clone(CloneInfo ii) {
return new ConstMissingInstr(ii.getRenamedVariable(result), receiver.cloneForInlining(ii), missingConst);

This file was deleted.

Original file line number Diff line number Diff line change
@@ -25,10 +25,6 @@ public String toString() {
return super.toString() + "{1F}";
}

public Operand getReceiver() {
return receiver;
}

public long getFixnumArg() {
return fixNum;
}
Original file line number Diff line number Diff line change
@@ -19,10 +19,6 @@ public String toString() {
return super.toString() + "{1OB}";
}

public Operand getReceiver() {
return receiver;
}

public Operand getArg1() {
return getCallArgs()[0];
}
Original file line number Diff line number Diff line change
@@ -18,10 +18,6 @@ public String toString() {
return super.toString() + "{1O}";
}

public Operand getReceiver() {
return receiver;
}

public Operand getArg1() {
return getCallArgs()[0];
}
Original file line number Diff line number Diff line change
@@ -18,10 +18,6 @@ public String toString() {
return super.toString() + "{1O}";
}

public Operand getReceiver() {
return receiver;
}

public Operand getArg1() {
return getCallArgs()[0];
}
Original file line number Diff line number Diff line change
@@ -18,10 +18,6 @@ public String toString() {
return super.toString() + "{0O}";
}

public Operand getReceiver() {
return receiver;
}

@Override
public Object interpret(ThreadContext context, StaticScope currScope, DynamicScope dynamicScope, IRubyObject self, Object[] temp) {
IRubyObject object = (IRubyObject) receiver.retrieve(context, self, currScope, dynamicScope, temp);
15 changes: 8 additions & 7 deletions core/src/main/java/org/jruby/ir/interpreter/Interpreter.java
Original file line number Diff line number Diff line change
@@ -78,7 +78,7 @@ public static IRubyObject interpretCommonEval(Ruby runtime, String file, int lin
StaticScope ss = rootNode.getStaticScope();
IRScope containingIRScope = getEvalContainerScope(ss, evalType);
IREvalScript evalScript = IRBuilder.createIRBuilder(runtime, runtime.getIRManager()).buildEvalRoot(ss, containingIRScope, file, lineNumber, rootNode, evalType);
evalScript.prepareForInterpretation(false);
evalScript.prepareForInterpretation();
ThreadContext context = runtime.getCurrentContext();

IRubyObject rv = null;
@@ -121,7 +121,7 @@ public static void runBeginEndBlocks(List<IRClosure> beBlocks, ThreadContext con

for (IRClosure b: beBlocks) {
// SSS FIXME: Should I piggyback on WrappedIRClosure.retrieve or just copy that code here?
b.prepareForInterpretation(false);
b.prepareForInterpretation();
Block blk = (Block)(new WrappedIRClosure(b.getSelf(), b)).retrieve(context, self, currScope, context.getCurrentScope(), temp);
blk.yield(context, null);
}
@@ -525,12 +525,13 @@ private static DynamicScope getNewDynScope(ThreadContext context, IRScope scope,

private static IRubyObject interpret(ThreadContext context, IRubyObject self,
IRScope scope, Visibility visibility, RubyModule implClass, String name, IRubyObject[] args, Block block, Block.Type blockType) {
Instr[] instrs = scope.getInstrsForInterpretation(blockType == Block.Type.LAMBDA);
int numTempVars = scope.getTemporaryVariablesCount();
InterpreterContext interpreterContext = scope.getInstrsForInterpretation();
Instr[] instrs = interpreterContext.getInstructions();
int numTempVars = interpreterContext.getTemporaryVariablecount();
Object[] temp = numTempVars > 0 ? new Object[numTempVars] : null;
int numFloatVars = scope.getFloatVariablesCount();
int numFixnumVars = scope.getFixnumVariablesCount();
int numBooleanVars = scope.getBooleanVariablesCount();
int numFloatVars = interpreterContext.getTemporaryFloatVariablecount();
int numFixnumVars = interpreterContext.getTemporaryFixnumVariablecount();
int numBooleanVars = interpreterContext.getTemporaryBooleanVariablecount();
double[] floats = numFloatVars > 0 ? new double[numFloatVars] : null;
long[] fixnums = numFixnumVars > 0 ? new long[numFixnumVars] : null;
boolean[] booleans = numBooleanVars > 0 ? new boolean[numBooleanVars] : null;
6 changes: 3 additions & 3 deletions core/src/main/java/org/jruby/ir/interpreter/Profiler.java
Original file line number Diff line number Diff line change
@@ -162,7 +162,7 @@ public int compare(IRCallSite a, IRCallSite b) {

IRScope tgtMethod = ircs.tgtM.getIRMethod();

Instr[] instrs = tgtMethod.getInstrsForInterpretation();
Instr[] instrs = tgtMethod.getInstrsForInterpretation().getInstructions();
// Dont inline large methods -- 500 is arbitrary
// Can be null if a previously inlined method hasn't been rebuilt
if ((instrs == null) || instrs.length > 500) {
@@ -228,8 +228,8 @@ public int compare(IRScope a, IRScope b) {
if (bden == 0) bden = 1;

// Use estimated instr count to order scopes -- rather than raw thread-poll count
float aCount = scopeThreadPollCounts.get(a).count * (1.0f * a.getInstrsForInterpretation().length/aden);
float bCount = scopeThreadPollCounts.get(b).count * (1.0f * b.getInstrsForInterpretation().length/bden);
float aCount = scopeThreadPollCounts.get(a).count * (1.0f * a.getInstrsForInterpretation().getInstructions().length/aden);
float bCount = scopeThreadPollCounts.get(b).count * (1.0f * b.getInstrsForInterpretation().getInstructions().length/bden);
if (aCount == bCount) return 0;
return (aCount < bCount) ? 1 : -1;
}
67 changes: 67 additions & 0 deletions core/src/main/java/org/jruby/ir/operands/InterpreterContext.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package org.jruby.ir.operands;

import java.util.EnumSet;
import java.util.List;
import org.jruby.ir.IRFlags;
import org.jruby.ir.instructions.Instr;
import org.jruby.ir.transformations.inlining.CloneInfo;

/**
*
*/
public class InterpreterContext extends Operand {
private int temporaryVariablecount;
private int temporaryBooleanVariablecount;
private int temporaryFixnumVariablecount;
private int temporaryFloatVariablecount;

private EnumSet<IRFlags> flags;

private Instr[] instructions;

public InterpreterContext(int temporaryVariablecount, int temporaryBooleanVariablecount,
int temporaryFixnumVariablecount, int temporaryFloatVariablecount,
EnumSet<IRFlags> flags, Instr[] instructions) {
super(null);

this.temporaryVariablecount = temporaryVariablecount;
this.temporaryBooleanVariablecount = temporaryBooleanVariablecount;
this.temporaryFixnumVariablecount = temporaryFixnumVariablecount;
this.temporaryFloatVariablecount = temporaryFloatVariablecount;
this.flags = flags;
this.instructions = instructions;
}

@Override
public void addUsedVariables(List<Variable> l) {}

@Override
public Operand cloneForInlining(CloneInfo ii) {
throw new IllegalStateException("Should not clone interp context");
}


public int getTemporaryVariablecount() {
return temporaryVariablecount;
}

public int getTemporaryBooleanVariablecount() {
return temporaryBooleanVariablecount;
}

public int getTemporaryFixnumVariablecount() {
return temporaryFixnumVariablecount;
}

public int getTemporaryFloatVariablecount() {
return temporaryFloatVariablecount;
}

public EnumSet<IRFlags> getFlags() {
return flags;
}

public Instr[] getInstructions() {
return instructions;
}
}
1 change: 1 addition & 0 deletions core/src/main/java/org/jruby/ir/operands/Label.java
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@
// there is exactly one label object with the same label string?
public class Label extends Operand {
public static final Label UNRESCUED_REGION_LABEL = new Label("UNRESCUED_REGION", 0);
public static final Label GLOBAL_ENSURE_BLOCK_LABEL = new Label("_GLOBAL_ENSURE_BLOCK_", 0);

public final String prefix;
public final int id;
Original file line number Diff line number Diff line change
@@ -50,7 +50,6 @@ public Object execute(IRScope scope, Object... data) {
boolean bindingHasEscaped = scope.bindingHasEscaped();

CFG cfg = scope.cfg();
BasicBlock geb = cfg.getGlobalEnsureBB();

if (slvpp != null && bindingHasEscaped) {
scopeHasLocalVarStores = slvpp.scopeHasLocalVarStores();
@@ -123,9 +122,10 @@ public Object execute(IRScope scope, Object... data) {
if (requireBinding) entryBB.addInstr(new PushBindingInstr());

// Allocate GEB if necessary for popping
BasicBlock geb = cfg.getGlobalEnsureBB();
if (geb == null) {
Variable exc = scope.createTemporaryVariable();
geb = new BasicBlock(cfg, new Label("_GLOBAL_ENSURE_BLOCK", 0));
geb = new BasicBlock(cfg, Label.GLOBAL_ENSURE_BLOCK_LABEL);
geb.addInstr(new ReceiveJRubyExceptionInstr(exc)); // JRuby Implementation exception handling
geb.addInstr(new ThrowExceptionInstr(exc));
cfg.addGlobalEnsureBB(geb);
3 changes: 3 additions & 0 deletions core/src/main/java/org/jruby/ir/representations/CFG.java
Original file line number Diff line number Diff line change
@@ -408,6 +408,9 @@ private BasicBlock buildExitBasicBlock(Stack<ExceptionRegion> nestedExceptionReg
private BasicBlock createBB(Label label, Stack<ExceptionRegion> nestedExceptionRegions) {
BasicBlock basicBlock = new BasicBlock(this, label);
addBasicBlock(basicBlock);
if (label.equals(Label.GLOBAL_ENSURE_BLOCK_LABEL)) {
globalEnsureBB = basicBlock;
}

if (!nestedExceptionRegions.empty()) nestedExceptionRegions.peek().addBB(basicBlock);

10 changes: 6 additions & 4 deletions core/src/main/java/org/jruby/ir/runtime/IRRuntimeHelpers.java
Original file line number Diff line number Diff line change
@@ -112,13 +112,13 @@ public static IRubyObject handleNonlocalReturn(StaticScope scope, DynamicScope d
} else {
IRReturnJump rj = (IRReturnJump)rjExc;

// - If we are in a lambda or if we are in the method scope we are supposed to return from, stop propagating
// If we are in a lambda or if we are in the method scope we are supposed to return from, stop propagating.
if (inNonMethodBodyLambda(scope, blockType) || (rj.methodToReturnFrom == dynScope)) {
if (isDebug()) System.out.println("---> Non-local Return reached target in scope: " + dynScope);
if (isDebug()) System.out.println("---> Non-local Return reached target in scope: " + dynScope + " matching dynscope? " + (rj.methodToReturnFrom == dynScope));
return (IRubyObject) rj.returnValue;
}

// - If not, Just pass it along!
// If not, Just pass it along!
throw rj;
}
}
@@ -152,7 +152,9 @@ public static IRubyObject handleBreakAndReturnsInLambdas(ThreadContext context,
if ((exc instanceof IRBreakJump) && inNonMethodBodyLambda(scope, blockType)) {
// We just unwound all the way up because of a non-local break
throw IRException.BREAK_LocalJumpError.getException(context.getRuntime());
} else if (exc instanceof IRReturnJump) {
} else if (exc instanceof IRReturnJump && (blockType == null || inLambda(blockType))) {
// Ignore non-local return processing in non-lambda blocks.
// Methods have a null blocktype
return handleNonlocalReturn(scope, dynScope, exc, blockType);
} else {
// Propagate
11 changes: 4 additions & 7 deletions core/src/main/java/org/jruby/runtime/InterpretedIRBlockBody.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
package org.jruby.runtime;

import org.jruby.EvalType;
import org.jruby.RubyModule;
import org.jruby.runtime.Block;
import org.jruby.runtime.Binding;
import org.jruby.runtime.ThreadContext;
import org.jruby.ir.operands.InterpreterContext;
import org.jruby.ir.IRClosure;
import org.jruby.ir.IRFlags;
import org.jruby.ir.interpreter.Interpreter;
@@ -31,9 +28,9 @@ public InterpretedIRBlockBody(IRClosure closure, Arity arity, int argumentType)
public void ensureInstrsReady(Block.Type blockType) {
// Prepare closure if not yet done so we know if the method requires a dynscope or not
if (closure.getInstrsForInterpretation() == null) {
closure.prepareForInterpretation(blockType == Block.Type.LAMBDA);
this.pushScope = !closure.getFlags().contains(IRFlags.DYNSCOPE_ELIMINATED);
this.reuseParentScope = closure.getFlags().contains(IRFlags.REUSE_PARENT_DYNSCOPE);
InterpreterContext context = closure.prepareForInterpretation();
this.pushScope = !context.getFlags().contains(IRFlags.DYNSCOPE_ELIMINATED);
this.reuseParentScope = context.getFlags().contains(IRFlags.REUSE_PARENT_DYNSCOPE);
if (IRRuntimeHelpers.isDebug()) {
LOG.info("Executing '" + closure + "'");
// The base IR may not have been processed yet
42 changes: 22 additions & 20 deletions core/src/main/java/org/jruby/truffle/nodes/core/ArrayNodes.java
Original file line number Diff line number Diff line change
@@ -3083,20 +3083,21 @@ public RubyArray sortNull(RubyArray array) {
public RubyArray sortVeryShortIntegerFixnum(VirtualFrame frame, RubyArray array) {
final int[] store = (int[]) array.getStore();

// Insertion sort

final int size = array.getSize();

for (int i = 1; i < RubyContext.ARRAYS_SMALL; i++) {
// Selection sort - written very carefully to allow PE

for (int i = 0; i < RubyContext.ARRAYS_SMALL; i++) {
if (i < size) {
final int x = store[i];
int j = i;
// TODO(CS): node for this cast
while (j > 0 && (int) compareDispatchNode.call(frame, store[j - 1], "<=>", null, x) > 0) {
store[j] = store[j - 1];
j--;
for (int j = i + 1; j < RubyContext.ARRAYS_SMALL; j++) {
if (j < size) {
if ((int) compareDispatchNode.call(frame, store[j], "<=>", null, store[i]) < 0) {
final int temp = store[j];
store[j] = store[i];
store[i] = temp;
}
}
}
store[j] = x;
}
}

@@ -3118,20 +3119,21 @@ public RubyArray sortIntegerFixnum(VirtualFrame frame, RubyArray array) {
public RubyArray sortVeryShortLongFixnum(VirtualFrame frame, RubyArray array) {
final long[] store = (long[]) array.getStore();

// Insertion sort

final int size = array.getSize();

for (int i = 1; i < RubyContext.ARRAYS_SMALL; i++) {
// Selection sort - written very carefully to allow PE

for (int i = 0; i < RubyContext.ARRAYS_SMALL; i++) {
if (i < size) {
final long x = store[i];
int j = i;
// TODO(CS): node for this cast
while (j > 0 && (int) compareDispatchNode.call(frame, store[j - 1], "<=>", null, x) > 0) {
store[j] = store[j - 1];
j--;
for (int j = i + 1; j < RubyContext.ARRAYS_SMALL; j++) {
if (j < size) {
if ((int) compareDispatchNode.call(frame, store[j], "<=>", null, store[i]) < 0) {
final long temp = store[j];
store[j] = store[i];
store[i] = temp;
}
}
}
store[j] = x;
}
}

Original file line number Diff line number Diff line change
@@ -16,6 +16,7 @@
import org.jruby.truffle.runtime.core.*;
import org.jruby.truffle.runtime.methods.MethodLike;

import java.util.Arrays;
import java.util.Iterator;

/**
@@ -56,13 +57,7 @@ public static RubyProc getBlock(Object[] arguments) {
}

public static Object[] extractUserArguments(Object[] arguments) {
final Object[] userArguments = new Object[arguments.length - RUNTIME_ARGUMENT_COUNT];

for (int n = 0; n < userArguments.length; n++) {
userArguments[n] = arguments[RUNTIME_ARGUMENT_COUNT + n];
}

return userArguments;
return Arrays.copyOfRange(arguments, RUNTIME_ARGUMENT_COUNT, arguments.length);
}

public static Object[] concatUserArguments(Object o, Object[] arguments) {
4 changes: 4 additions & 0 deletions test/pom.rb
Original file line number Diff line number Diff line change
@@ -173,6 +173,8 @@
'<arg value="-Xparser.warn.argument_prefix=false" />' +
'<arg value="-T" />' +
'<arg value="-J-ea" />' +
'<arg value="-T" />' +
'<arg value="-J-Xmx2G" />' +
'<arg value="--config" />' +
'<arg value="spec/truffle/truffle.mspec" />' +
'<arg value="--excl-tag" />' +
@@ -221,6 +223,8 @@
'<arg value="-Xparser.warn.argument_prefix=false" />' +
'<arg value="-T" />' +
'<arg value="-J-ea" />' +
'<arg value="-T" />' +
'<arg value="-J-Xmx2G" />' +
'<arg value="--config" />' +
'<arg value="spec/truffle/truffle.mspec" />' +
'<arg value="--excl-tag" />' +
4 changes: 4 additions & 0 deletions test/pom.xml
Original file line number Diff line number Diff line change
@@ -317,6 +317,8 @@
<arg value="-Xparser.warn.argument_prefix=false" />
<arg value="-T" />
<arg value="-J-ea" />
<arg value="-T" />
<arg value="-J-Xmx2G" />
<arg value="--config" />
<arg value="spec/truffle/truffle.mspec" />
<arg value="--excl-tag" />
@@ -375,6 +377,8 @@
<arg value="-Xparser.warn.argument_prefix=false" />
<arg value="-T" />
<arg value="-J-ea" />
<arg value="-T" />
<arg value="-J-Xmx2G" />
<arg value="--config" />
<arg value="spec/truffle/truffle.mspec" />
<arg value="--excl-tag" />
18 changes: 18 additions & 0 deletions test/truffle/pe/core/array_pe.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
PETests.tests do

describe "A small Array" do

example "indexed by a constant" do
array = [3, 1, 2]
truffle_assert_constant array[1]
end

example "sorted and indexed" do
array = [3, 1, 2]
sorted = array.sort
truffle_assert_constant sorted[1]
end

end

end
61 changes: 60 additions & 1 deletion test/truffle/pe/core/fixnum_pe.rb
Original file line number Diff line number Diff line change
@@ -4,14 +4,73 @@

example "literal" do
truffle_assert_constant 14
truffle_assert_constant 0xffffffffffff
end

describe "#+" do

example "a Fixnum" do
truffle_assert_constant 14 + 2
end

counter_example "a Bignum" do
truffle_assert_constant 14 + 0xfffffffffffffffffffffffffffffff
end

example "a Float" do
truffle_assert_constant 14 + 2.0
end

counter_example "rand" do
truffle_assert_constant 14 + rand
end

end

describe "#*" do

example "a Fixnum" do
truffle_assert_constant 14 * 2
end

counter_example "a Bignum" do
truffle_assert_constant 14 * 0xfffffffffffffffffffffffffffffff
end

example "a Float" do
truffle_assert_constant 14 * 2.0
end

counter_example "rand" do
truffle_assert_constant 14 * rand
end

end

describe "#/" do

example "a Fixnum" do
truffle_assert_constant 14 / 2
end

example "a Bignum" do
truffle_assert_constant 14 / 0xfffffffffffffffffffffffffffffff
end

example "a Float" do
truffle_assert_constant 14 / 2.0
end

counter_example "rand" do
truffle_assert_constant 14 / rand
end

end

describe "#<=>" do

example "a Fixnum" do
truffle_assert_constant 14 <=> 2
end

end

62 changes: 61 additions & 1 deletion test/truffle/pe/core/float_pe.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,70 @@
PETests.tests do

describe "A Fixnum" do
describe "A Float" do

example "literal" do
truffle_assert_constant 14.2
end

describe "#+" do

example "a Fixnum" do
truffle_assert_constant 14.0 + 2
end

counter_example "a Bignum" do
truffle_assert_constant 14.0 + 0xfffffffffffffffffffffffffffffff
end

example "a Float" do
truffle_assert_constant 14.0 + 2.0
end

counter_example "rand" do
truffle_assert_constant 14.0 + rand
end

end

describe "#*" do

example "a Fixnum" do
truffle_assert_constant 14.0 * 2
end

counter_example "a Bignum" do
truffle_assert_constant 14.0 * 0xfffffffffffffffffffffffffffffff
end

example "a Float" do
truffle_assert_constant 14.0 * 2.0
end

counter_example "rand" do
truffle_assert_constant 14.0 * rand
end

end

describe "#/" do

example "a Fixnum" do
truffle_assert_constant 14.0 / 2
end

counter_example "a Bignum" do
truffle_assert_constant 14.0 / 0xfffffffffffffffffffffffffffffff
end

example "a Float" do
truffle_assert_constant 14.0 / 2.0
end

counter_example "rand" do
truffle_assert_constant 14.0 / rand
end

end

end

18 changes: 18 additions & 0 deletions test/truffle/pe/core/hash_pe.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
PETests.tests do

describe "A small Hash" do

example "indexed by a constant" do
hash = {a: 0, b: 1, c: 2}
truffle_assert_constant hash[:b]
end

example "mapped to an Array and indexed by a constant" do
hash = {a: 0, b: 1, c: 2}
array = hash.map { |k, v| v }
truffle_assert_constant array[0]
end

end

end
33 changes: 33 additions & 0 deletions test/truffle/pe/core/kernel/set_trace_func_pe.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
module KernelSetTraceFuncFixtures

def self.method_with_many_lines(a, b)
x = a
y = b
z = x + y
z
end

end

PETests.tests do

describe "Kernel" do

describe "set_trace_func" do

begin
set_trace_func proc { |event, file, line, id, binding, classname|
}

broken_example "still has Fixnum#+ Fixnum" do
truffle_assert_constant KernelSetTraceFuncFixtures.method_with_many_lines(14, 2)
end
ensure
set_trace_func nil
end

end

end

end
8 changes: 8 additions & 0 deletions test/truffle/pe/core/symbol_pe.rb
Original file line number Diff line number Diff line change
@@ -5,6 +5,14 @@
example "literal" do
truffle_assert_constant :foo
end

example "#== a Symbol" do
truffle_assert_constant :foo == :foo
end

example "#!= a Symbol" do
truffle_assert_constant :foo != :bar
end

end

38 changes: 38 additions & 0 deletions test/truffle/pe/language/metaprogramming_pe.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
module MetaprogrammingFixtures

class MethodMissing

def method_missing(method, *args)
14
end

end

class ClassWithExistingMethod

def existing_method(a)
a
end

end

end

PETests.tests do

example "A call that results in #method_missing" do
method_missing = MetaprogrammingFixtures::MethodMissing.new
truffle_assert_constant method_missing.does_not_exist
end

example "#respond_to? on a method that does exist" do
object_with_existing_method = MetaprogrammingFixtures::ClassWithExistingMethod.new
truffle_assert_constant object_with_existing_method.respond_to? :existing_method
end

example "#send on a method that exists using a symbol" do
object_with_existing_method = MetaprogrammingFixtures::ClassWithExistingMethod.new
truffle_assert_constant object_with_existing_method.send(:existing_method, 14)
end

end
37 changes: 37 additions & 0 deletions test/truffle/pe/macro/pushing_pixels_pe.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
module PushingPixelsFixtures

module Foo
extend self

def foo(a, b, c)
hash = {a: a, b: b, c: c}
array = hash.map { |k, v| v }
x = array[0]
y = [a, b, c].sort[1]
x + y
end

end

class Bar

def method_missing(method, *args)
if Foo.respond_to?(method)
Foo.send(method, *args)
else
0
end
end

end

end

PETests.tests do

example "A set of constants used in a literal hash, mapped to an array, indexed, used in an array literal, sorted, indexed, and added, all via method_missing, respond_to? and send" do
bar = PushingPixelsFixtures::Bar.new
truffle_assert_constant bar.foo(14, 8, 6)
end

end
14 changes: 14 additions & 0 deletions test/truffle/pe/pe.rb
Original file line number Diff line number Diff line change
@@ -54,6 +54,15 @@ def self.counter_example(description)
#end
end

def self.broken_example(description)
puts "warning: broken examples not run"
#describe "#{description} is not constant" do
# inner_example do
# yield
# end
#end
end

def self.finish
print "\n"

@@ -112,10 +121,15 @@ def self.inner_example

$: << File.expand_path('..', __FILE__)

require "language/metaprogramming_pe.rb"
require "core/truefalse_pe.rb"
require "core/fixnum_pe.rb"
require "core/float_pe.rb"
require "core/symbol_pe.rb"
require "core/array_pe.rb"
require "core/hash_pe.rb"
require "core/kernel/set_trace_func_pe.rb"
require "macro/pushing_pixels_pe.rb"

# Finished

0 comments on commit b7f248b

Please sign in to comment.