Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: f0976402558b
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: e22fa40e0544
Choose a head ref
  • 5 commits
  • 6 files changed
  • 2 contributors

Commits on Jan 17, 2015

  1. Unboxing: Few more fixes

    * resemblesALUOp, getUnboxedOp, getUnboxedResultType should all
      be in agreement about what operations they want to simplify.
    subbuss committed Jan 17, 2015
    Copy the full SHA
    2bf0d43 View commit details
  2. Unboxing: First pass generalizing and cleaning up unboxable ops info

    * Pulled together related info about unboxable ops into their own
      classes to enable easy additions of newer unboxable ops in the future.
    
    * This eliminates a whole bunch of awkward buggy hardcoded checks and
      tests from the unboxing analysis.
    subbuss committed Jan 17, 2015
    Copy the full SHA
    80d3fff View commit details
  3. Copy the full SHA
    41e4586 View commit details

Commits on Jan 18, 2015

  1. Copy the full SHA
    ae54026 View commit details
  2. Copy the full SHA
    e22fa40 View commit details
Original file line number Diff line number Diff line change
@@ -154,69 +154,6 @@ private void setOperandType(UnboxState state, Variable v, Class newType) {
}
}

private boolean resemblesALUOp(String name) {
return name.equals("+") || name.equals("-") || name.equals("*") || name.equals("/") ||
name.equals("|") || name.equals("&") || name.equals(">>") || name.equals("<<") ||
name.equals(">") || name.equals("<") || name.equals("==") || name.equals("!=");
}

private Class getUnboxedResultType(Class operandType, String name) {
if (name.length() == 1) {
switch (name.charAt(0)) {
case '+' :
case '-' :
case '*' :
case '/' : return operandType == Float.class ? Float.class : operandType == Fixnum.class ? Fixnum.class : null;
case '>' :
case '<' : return operandType == Float.class || operandType == Fixnum.class ? Boolean.class : null;
default : return null;
}
} else if (name.equals(">>") || name.equals("<<")) {
return Fixnum.class;
} else if (name.equals("==") || name.equals("===")) {
return Boolean.class;
} else {
return null;
}
}

private Operation getUnboxedOp(Class unboxedType, String name) {
if (unboxedType == Float.class) {
if (name.length() == 1) {
switch (name.charAt(0)) {
case '+' : return Operation.FADD;
case '-' : return Operation.FSUB;
case '*' : return Operation.FMUL;
case '/' : return Operation.FDIV;
case '>' : return Operation.FGT;
case '<' : return Operation.FLT;
}
} else if (name.equals("==") || name.equals("===")) {
return Operation.FEQ;
}
} else if (unboxedType == Fixnum.class) {
if (name.length() == 1) {
switch (name.charAt(0)) {
case '+' : return Operation.IADD;
case '-' : return Operation.ISUB;
case '*' : return Operation.IMUL;
case '/' : return Operation.IDIV;
case '>' : return Operation.IGT;
case '<' : return Operation.ILT;
case '|' : return Operation.IOR;
case '&' : return Operation.IAND;
case '^' : return Operation.IXOR;
}
} else {
if (name.equals(">>")) return Operation.ISHR;
if (name.equals("<<")) return Operation.ISHL;
if (name.equals("==") || name.equals("===")) return Operation.IEQ;
}
}

return null;
}

private void markLocalVariables(Collection<Variable> varsToBox, Set<Variable> varsToCheck) {
for (Variable v: varsToCheck) {
if (v instanceof LocalVariable) varsToBox.add(v);
@@ -306,36 +243,32 @@ public void applyTransferFunction(Instr i) {
// should ideally be done 'on-demand'. This indicates that this could
// be a backward-flow algo OR that this algo should be run on a
// dataflow graph / SSA graph.
if (srcType == Float.class) {
if (srcType == Float.class || srcType == Fixnum.class) {
unboxedAndDirty = true;
tmpState.unboxedVars.put(dst, srcType);
} else if (srcType == Fixnum.class) {
unboxedAndDirty = true;
tmpState.unboxedVars.put(dst, srcType);
}

tmpState.unboxedVars.put(dst, dstType);
} else if (i instanceof ClosureAcceptingInstr) {
Operand o = ((ClosureAcceptingInstr)i).getClosureArg();
// Process calls specially -- these are what we want to optimize!
if (i instanceof CallBase && o == null) {
CallBase c = (CallBase)i;
String m = c.getName();
Operand r = c.getReceiver();
if (dst != null && c.getArgsCount() == 1 && resemblesALUOp(m)) {
if (dst != null && c.getArgsCount() == 1 && problem.isUnboxableMethod(m)) {
Operand a = c.getArg1();
Class receiverType = getOperandType(tmpState, r);
Class argType = getOperandType(tmpState, a);
// Optimistically assume that call is an ALU op
if (receiverType == Float.class ||
(receiverType == Fixnum.class && (argType == Float.class || argType == Fixnum.class)))
{
Class unboxedType = (receiverType == Float.class || argType == Float.class) ? Float.class : Fixnum.class;

if (problem.acceptsArgTypes(m, receiverType, argType)) {
Class unboxedType = problem.getUnboxedType(m, receiverType, argType);
unboxedAndDirty = true;

dstType = getUnboxedResultType(unboxedType, m);
dstType = problem.getUnboxedResultType(m, unboxedType);
tmpState.unboxedVars.put(dst, dstType);

// If 'r' and 'a' are not already in unboxed forms at this point,
// they will get unboxed after this, because we want to opt. this call
// they will get unboxed after this, because we want to opt. this call.
if (r instanceof Variable) {
tmpState.unboxedVars.put((Variable)r, unboxedType);
}
@@ -478,34 +411,33 @@ private Operand unboxOperand(UnboxState state, Class reqdType, Map<Variable, Tem
}

return unboxedVar;
} else {
if (arg instanceof Float) {
return new UnboxedFloat(((Float)arg).getValue());
} else if (arg instanceof Fixnum) {
return new UnboxedFixnum(((Fixnum)arg).getValue());
} else if (arg instanceof Boolean) {
return new UnboxedBoolean(((Boolean)arg).isTrue());
}
// This has to be a known operand like (UnboxedBoolean, etc.)
return arg;
} else if (arg instanceof Float) {
return new UnboxedFloat(((Float)arg).getValue());
} else if (arg instanceof Fixnum) {
return new UnboxedFixnum(((Fixnum)arg).getValue());
} else if (arg instanceof Boolean) {
return new UnboxedBoolean(((Boolean)arg).isTrue());
}

// This has to be a known operand like (UnboxedBoolean, etc.)
return arg;
}

private Operand getUnboxedOperand(UnboxState state, Map<Variable, TemporaryLocalVariable> unboxMap, Operand arg) {
if (arg instanceof Variable) {
Variable v = (Variable)arg;
Class unboxedType = state.unboxedVars.get(v);
return unboxedType == null ? arg : getUnboxedVar(unboxedType, unboxMap, v);
} else {
if (arg instanceof Float) {
return new UnboxedFloat(((Float)arg).getValue());
} else if (arg instanceof Fixnum) {
return new UnboxedFixnum(((Fixnum)arg).getValue());
} else if (arg instanceof Boolean) {
return new UnboxedBoolean(((Boolean)arg).isTrue());
}
return arg;
} else if (arg instanceof Float) {
return new UnboxedFloat(((Float)arg).getValue());
} else if (arg instanceof Fixnum) {
return new UnboxedFixnum(((Fixnum)arg).getValue());
} else if (arg instanceof Boolean) {
return new UnboxedBoolean(((Boolean)arg).isTrue());
}

// This has to be a known operand like (UnboxedBoolean, etc.)
return arg;
}

private void boxRequiredVars(Instr i, UnboxState state, Map<Variable, TemporaryLocalVariable> unboxMap, Variable dst, boolean hasRescuer, boolean isDFBarrier, List<Instr> newInstrs) {
@@ -678,34 +610,32 @@ public void unbox(Map<Variable, TemporaryLocalVariable> unboxMap) {
Operand unboxedSrc = src instanceof Variable ? getUnboxedVar(srcType, unboxMap, (Variable)src) : src;
TemporaryLocalVariable unboxedDst = getUnboxedVar(srcType, unboxMap, dst);
newInstrs.add(new CopyInstr(Operation.COPY, unboxedDst, unboxedSrc));
tmpState.unboxedVars.put(dst, srcType);
unboxedAndDirty = true;
}

tmpState.unboxedVars.put(dst, dstType);
} else if (i instanceof ClosureAcceptingInstr) {
Operand o = ((ClosureAcceptingInstr)i).getClosureArg();
if (i instanceof CallBase && o == null) {
CallBase c = (CallBase)i;
String m = c.getName();
Operand r = c.getReceiver();
if (dst != null && c.getArgsCount() == 1 && resemblesALUOp(m)) {
if (dst != null && c.getArgsCount() == 1 && problem.isUnboxableMethod(m)) {
Operand a = c.getArg1();
Class receiverType = getOperandType(tmpState, r);
Class argType = getOperandType(tmpState, a);

// Optimistically assume that call is an ALU op
Operation unboxedOp = null;
Class unboxedType = null;
if (receiverType == Float.class ||
(receiverType == Fixnum.class && (argType == Float.class || argType == Fixnum.class)))
{
unboxedType = (receiverType == Float.class || argType == Float.class) ? Float.class : Fixnum.class;
unboxedOp = getUnboxedOp(unboxedType, m);
if (problem.acceptsArgTypes(m, receiverType, argType)) {
unboxedType = problem.getUnboxedType(m, receiverType, argType);
unboxedOp = problem.getUnboxedOp(m, unboxedType);
}

if (unboxedType != null && unboxedOp != null) {
unboxedAndDirty = true;

dstType = getUnboxedResultType(unboxedType, m);
dstType = problem.getUnboxedResultType(m, unboxedType);
tmpState.unboxedVars.put(dst, dstType);

TemporaryLocalVariable unboxedDst = getUnboxedVar(dstType, unboxMap, dst);
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package org.jruby.ir.dataflow.analyses;

import org.jruby.ir.dataflow.DataFlowConstants;
import org.jruby.ir.dataflow.DataFlowProblem;
import org.jruby.ir.operands.Fixnum;
import org.jruby.ir.operands.Float;
import org.jruby.ir.operands.TemporaryLocalVariable;
import org.jruby.ir.operands.Variable;
import org.jruby.ir.Operation;
import org.jruby.ir.representations.BasicBlock;

import java.util.HashMap;
@@ -24,6 +26,124 @@
//
// Type of a variable will change at most twice: TOP --> class --> BOTTOM

abstract class UnboxableOp {
public final String name;

public UnboxableOp(String name) {
this.name = name;
}

abstract public boolean acceptsArgTypes(Class receiverType, Class argType);
abstract public Class getUnboxedType(Class receiverType, Class argType);
abstract public Class getUnboxedResultType(Class operandType);
abstract public Operation getUnboxedOp(Class operandType);

public static Map<String, UnboxableOp> opsMap = new HashMap<String, UnboxableOp>();

public static void addUnboxableOp(UnboxableOp op) {
opsMap.put(op.name, op);
}

static {
addUnboxableOp(new ArithOp("+", Operation.IADD, Operation.FADD));
addUnboxableOp(new ArithOp("-", Operation.ISUB, Operation.FSUB));
addUnboxableOp(new ArithOp("*", Operation.IMUL, Operation.FMUL));
addUnboxableOp(new ArithOp("/", Operation.IDIV, Operation.FDIV));
addUnboxableOp(new LogicOp("|", Operation.IOR));
addUnboxableOp(new LogicOp("&", Operation.IAND));
addUnboxableOp(new LogicOp("^", Operation.IXOR));
addUnboxableOp(new LogicOp("<<", Operation.ISHL));
addUnboxableOp(new LogicOp(">>", Operation.ISHR));
addUnboxableOp(new CompareOp("<", Operation.ILT, Operation.FLT));
addUnboxableOp(new CompareOp(">", Operation.IGT, Operation.FGT));
addUnboxableOp(new CompareOp("==", Operation.IEQ, Operation.FEQ));
addUnboxableOp(new CompareOp("===", Operation.IEQ, Operation.FEQ));
}
}

final class ArithOp extends UnboxableOp {
private final Operation fixnumOp;
private final Operation floatOp;

public ArithOp(String name, Operation fixnumOp, Operation floatOp) {
super(name);
this.fixnumOp = fixnumOp;
this.floatOp = floatOp;
}

public boolean acceptsArgTypes(Class receiverType, Class argType) {
return receiverType == Float.class ||
(receiverType == Fixnum.class && (argType == Float.class || argType == Fixnum.class));
}

public Class getUnboxedType(Class receiverType, Class argType) {
// acceptsArgTypes should have ensured that we have the right types here
return receiverType == Float.class || argType == Float.class ? Float.class : Fixnum.class;
}

public final Class getUnboxedResultType(Class operandType) {
return (operandType == Float.class || operandType == Fixnum.class) ? operandType : null;
}

public final Operation getUnboxedOp(Class operandType) {
return (operandType == Fixnum.class) ? fixnumOp : (operandType == Float.class) ? floatOp : null;
}
}

final class LogicOp extends UnboxableOp {
private final Operation op;

public LogicOp(String name, Operation op) {
super(name);
this.op = op;
}

public boolean acceptsArgTypes(Class receiverType, Class argType) {
return receiverType == Fixnum.class && argType == Fixnum.class;
}

public Class getUnboxedType(Class receiverType, Class argType) {
// acceptsArgTypes should have ensured that we have the right types here
return Fixnum.class;
}

public final Class getUnboxedResultType(Class operandType) {
return operandType == Fixnum.class ? Fixnum.class : null;
}

public final Operation getUnboxedOp(Class operandType) {
return operandType == Fixnum.class ? op : null;
}
}

final class CompareOp extends UnboxableOp {
private final Operation fixnumOp;
private final Operation floatOp;

public CompareOp(String name, Operation fixnumOp, Operation floatOp) {
super(name);
this.fixnumOp = fixnumOp;
this.floatOp = floatOp;
}

public boolean acceptsArgTypes(Class receiverType, Class argType) {
return receiverType == argType && (receiverType == Fixnum.class || receiverType == Float.class);
}

public Class getUnboxedType(Class receiverType, Class argType) {
// acceptsArgTypes should have ensured that we have the right types here
return receiverType;
}

public final Class getUnboxedResultType(Class operandType) {
return (operandType == Fixnum.class || operandType == Float.class) ? Boolean.class : null;
}

public final Operation getUnboxedOp(Class operandType) {
return (operandType == Fixnum.class) ? fixnumOp : (operandType == Float.class) ? floatOp : null;
}
}

public class UnboxableOpsAnalysisProblem extends DataFlowProblem<UnboxableOpsAnalysisProblem, UnboxableOpsAnalysisNode> {
public final static String NAME = "UnboxableOpsAnalysis";

@@ -54,4 +174,28 @@ public void unbox() {
n.unbox(unboxMap);
}
}

public boolean isUnboxableMethod(String name) {
return UnboxableOp.opsMap.get(name) != null;
}

public boolean acceptsArgTypes(String name, Class receiverType, Class argType) {
UnboxableOp uop = UnboxableOp.opsMap.get(name);
return uop == null ? false : uop.acceptsArgTypes(receiverType, argType);
}

public Class getUnboxedType(String name, Class receiverType, Class argType) {
UnboxableOp uop = UnboxableOp.opsMap.get(name);
return uop == null ? null : uop.getUnboxedType(receiverType, argType);
}

public Class getUnboxedResultType(String name, Class operandType) {
UnboxableOp uop = UnboxableOp.opsMap.get(name);
return uop == null ? null : uop.getUnboxedResultType(operandType);
}

public Operation getUnboxedOp(String name, Class operandType) {
UnboxableOp uop = UnboxableOp.opsMap.get(name);
return uop == null ? null : uop.getUnboxedOp(operandType);
}
}
3 changes: 3 additions & 0 deletions core/src/main/java/org/jruby/truffle/TruffleBridgeImpl.java
Original file line number Diff line number Diff line change
@@ -135,6 +135,9 @@ public void init() {
// Libraries copied unmodified from MRI
loadPath.slowPush(truffleContext.makeString(new File(home, "lib/ruby/truffle/mri").toString()));

// Our own implementations
loadPath.slowPush(truffleContext.makeString(new File(home, "lib/ruby/truffle/truffle").toString()));

// Shims
loadPath.slowPush(truffleContext.makeString(new File(home, "lib/ruby/truffle/shims").toString()));

Loading