package org.jruby.ir.dataflow.analyses;
import org.jruby.dirgra.Edge;
import org.jruby.ir.IRClosure;
import org.jruby.ir.Operation;
import org.jruby.ir.dataflow.FlowGraphNode;
import org.jruby.ir.instructions.*;
import org.jruby.ir.instructions.boxing.*;
import org.jruby.ir.interpreter.FullInterpreterContext;
import org.jruby.ir.operands.*;
import org.jruby.ir.operands.Boolean;
import org.jruby.ir.operands.Float;
import org.jruby.ir.representations.BasicBlock;
import org.jruby.ir.representations.CFG;
import java.util.*;
public class UnboxableOpsAnalysisNode extends FlowGraphNode<UnboxableOpsAnalysisProblem, UnboxableOpsAnalysisNode> {
private static class UnboxState {
Map<Variable, Class> types;
Map<Variable, Class> unboxedVars;
Set<Variable> unboxedDirtyVars;
public UnboxState() {
types = new HashMap<Variable, Class>();
unboxedVars = new HashMap<Variable, Class>();
unboxedDirtyVars = new HashSet<Variable>();
}
public UnboxState(UnboxState init) {
types = new HashMap<Variable, Class>(init.types);
unboxedVars = new HashMap<Variable, Class>(init.unboxedVars);
unboxedDirtyVars = new HashSet<Variable>(init.unboxedDirtyVars);
}
public void computeMEETForTypes(UnboxState other, boolean localVarsOnly) {
Map<Variable, Class> otherTypes = other.types;
for (Variable v: otherTypes.keySet()) {
if (!localVarsOnly || v instanceof LocalVariable) {
Class c1 = types.get(v);
Class c2 = otherTypes.get(v);
if (c1 == null) {
types.put(v, c2);
} else if (c1 != c2) {
types.put(v, Object.class);
}
}
}
}
public void computeMEETForUnboxedVars(UnboxState other) {
Map<Variable, Class> otherVars = other.unboxedVars;
for (Variable v: otherVars.keySet()) {
Class c1 = unboxedVars.get(v);
Class c2 = otherVars.get(v);
if (c1 == null) {
unboxedVars.put(v, c2);
} else if (c1 != c2) {
unboxedVars.remove(v);
}
}
}
public void computeMEET(UnboxState other) {
computeMEETForTypes(other, false);
computeMEETForUnboxedVars(other);
unboxedDirtyVars.addAll(other.unboxedDirtyVars);
}
public boolean equals(UnboxState other) {
return types.equals(other.types) &&
unboxedVars.equals(other.unboxedVars) &&
unboxedDirtyVars.equals(other.unboxedDirtyVars);
}
public void debugOut() {
System.out.print("-- Known types:");
for (Variable v: types.keySet()) {
if (types.get(v) != Object.class) {
System.out.println(v + "-->" + types.get(v));
}
}
System.out.print("-- Unboxed vars:");
for (Variable v: unboxedVars.keySet()) {
System.out.print(" " + v + "-->" + unboxedVars.get(v));
}
System.out.println("------");
System.out.print("-- Unboxed dirty vars:");
for (Variable v: unboxedDirtyVars) {
System.out.print(" " + v);
}
System.out.println("------");
}
}
public UnboxableOpsAnalysisNode(UnboxableOpsAnalysisProblem prob, BasicBlock n) {
super(prob, n);
}
@Override
public void init() {
outState = new UnboxState();
}
@Override
public void buildDataFlowVars(Instr i) {
}
@Override
public void applyPreMeetHandler() {
if (problem.getFIC().getScope() instanceof IRClosure && basicBlock.isEntryBB()) {
if (inState == null) {
inState = new UnboxState();
}
} else {
inState = new UnboxState();
}
}
@Override
public void compute_MEET(Edge e, UnboxableOpsAnalysisNode pred) {
if (!pred.basicBlock.isRescueEntry()) inState.computeMEET(pred.outState);
}
private Class getOperandType(UnboxState state, Operand o) {
if (o instanceof Float) {
return Float.class;
} else if (o instanceof Fixnum) {
return Fixnum.class;
} else if (o instanceof Bignum) {
return Bignum.class;
} else if (o instanceof Boolean) {
return Boolean.class;
} else if (o instanceof Variable) {
return state.types.get((Variable) o);
} else {
return null;
}
}
private void setOperandType(UnboxState state, Variable v, Class newType) {
if (v != null && newType != null) {
state.types.put(v, newType);
}
}
private void markLocalVariables(Collection<Variable> varsToBox, Set<Variable> varsToCheck) {
for (Variable v: varsToCheck) {
if (v instanceof LocalVariable) varsToBox.add(v);
}
}
private void updateUnboxedVarsInfoForBoxedInstr(Instr i, UnboxState state, Variable dst, boolean hasRescuer, boolean isDFBarrier) {
HashSet<Variable> varsToBox = new HashSet<Variable>();
if (i.canRaiseException()) {
if (hasRescuer) {
state.unboxedDirtyVars.clear();
} else if (problem.getFIC().getScope() instanceof IRClosure) {
markLocalVariables(varsToBox, state.unboxedDirtyVars);
state.unboxedDirtyVars.removeAll(varsToBox);
}
}
if (isDFBarrier) {
markLocalVariables(varsToBox, state.unboxedDirtyVars);
state.unboxedDirtyVars.removeAll(varsToBox);
List<Variable> lvs = new ArrayList<Variable>();
markLocalVariables(lvs, state.unboxedVars.keySet());
state.unboxedVars.keySet().removeAll(lvs);
}
Operation op = i.getOperation();
boolean isBranch = op == Operation.B_TRUE || op == Operation.B_FALSE;
if (!isBranch) {
state.unboxedDirtyVars.removeAll(i.getUsedVariables());
}
if (dst != null) {
state.unboxedVars.remove(dst);
state.unboxedDirtyVars.remove(dst);
}
}
@Override
public void initSolution() {
tmpState = new UnboxState(inState);
}
@Override
public void applyTransferFunction(Instr i) {
Variable dst = null;
Class dstType = Object.class;
boolean unboxedAndDirty = false;
boolean hitDFBarrier = false;
if (i instanceof ResultInstr) {
dst = ((ResultInstr)i).getResult();
}
if (i instanceof CopyInstr) {
Operand src = ((CopyInstr)i).getSource();
Class srcType = getOperandType(tmpState, src);
dstType = srcType;
if (srcType == Float.class || srcType == Fixnum.class) {
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.getId();
Operand r = c.getReceiver();
if (dst != null && c.getArgsCount() == 1 && problem.isUnboxableMethod(m)) {
Operand a = c.getArg1();
Class receiverType = getOperandType(tmpState, r);
Class argType = getOperandType(tmpState, a);
if (problem.acceptsArgTypes(m, receiverType, argType)) {
Class unboxedType = problem.getUnboxedType(m, receiverType, argType);
unboxedAndDirty = true;
dstType = problem.getUnboxedResultType(m, unboxedType);
tmpState.unboxedVars.put(dst, dstType);
if (r instanceof Variable) {
tmpState.unboxedVars.put((Variable)r, unboxedType);
}
if (a instanceof Variable) {
tmpState.unboxedVars.put((Variable)a, unboxedType);
}
} else if (c.targetRequiresCallersBinding()) {
hitDFBarrier = true;
}
}
} else if (o instanceof WrappedIRClosure) {
FullInterpreterContext clfic = ((WrappedIRClosure)o).getClosure().getFullInterpreterContext();
UnboxableOpsAnalysisProblem subProblem = (UnboxableOpsAnalysisProblem) clfic.getDataFlowProblems().get(UnboxableOpsAnalysisProblem.NAME);
if (subProblem == null) {
subProblem = new UnboxableOpsAnalysisProblem();
subProblem.setup(clfic);
clfic.getDataFlowProblems().put(UnboxableOpsAnalysisProblem.NAME, problem);
}
UnboxableOpsAnalysisNode exitNode = subProblem.getExitNode();
UnboxableOpsAnalysisNode entryNode = subProblem.getEntryNode();
entryNode.inState = new UnboxState();
for (Variable v: tmpState.types.keySet()) {
if (v instanceof LocalVariable) {
entryNode.inState.types.put(v, tmpState.types.get(v));
}
}
entryNode.inState.computeMEET(exitNode.outState);
subProblem.compute_MOP_Solution();
tmpState.computeMEETForTypes(exitNode.outState, true);
hitDFBarrier = true;
} else {
hitDFBarrier = true;
}
} else {
}
setOperandType(tmpState, dst, dstType);
if (unboxedAndDirty) {
tmpState.unboxedDirtyVars.add(dst);
} else {
updateUnboxedVarsInfoForBoxedInstr(i, tmpState, dst, hasExceptionsRescued(), hitDFBarrier);
}
}
@Override
public boolean solutionChanged() {
return !tmpState.equals(outState);
}
@Override
public void finalizeSolution() {
outState = tmpState;
}
private boolean matchingTypes(Class c, TemporaryVariableType t) {
switch (t) {
case FLOAT: return c == Float.class;
case FIXNUM: return c == Fixnum.class;
case BOOLEAN: return c == java.lang.Boolean.class;
default: return c != Float.class && c != java.lang.Boolean.class && c != Fixnum.class;
}
}
private TemporaryLocalVariable getUnboxedVar(Class reqdType, Map<Variable, TemporaryLocalVariable> unboxMap, Variable v, boolean createNew) {
TemporaryLocalVariable unboxedVar = unboxMap.get(v);
if ((unboxedVar == null && createNew) || !matchingTypes(reqdType, unboxedVar.getType())) {
unboxedVar = problem.getFIC().getNewUnboxedVariable(reqdType);
unboxMap.put(v, unboxedVar);
} else if (unboxedVar == null) {
System.out.println("ERROR: No unboxed var for : " + v);
}
return unboxedVar;
}
private TemporaryLocalVariable getUnboxedVar(Class reqdType, Map<Variable, TemporaryLocalVariable> unboxMap, Variable v) {
return getUnboxedVar(reqdType, unboxMap, v, true);
}
public void boxVar(UnboxState state, Class reqdType, Map<Variable, TemporaryLocalVariable> unboxMap, Variable v, List<Instr> newInstrs) {
TemporaryLocalVariable unboxedV = getUnboxedVar(reqdType, unboxMap, v);
TemporaryVariableType vType = unboxedV.getType();
if (vType == TemporaryVariableType.BOOLEAN) {
newInstrs.add(new BoxBooleanInstr(v, unboxedV));
} else if (vType == TemporaryVariableType.FLOAT) {
newInstrs.add(new BoxFloatInstr(v, unboxedV));
} else if (vType == TemporaryVariableType.FIXNUM) {
newInstrs.add(new BoxFixnumInstr(v, unboxedV));
}
state.unboxedDirtyVars.remove(v);
}
public void unboxVar(UnboxState state, Class reqdType, Map<Variable, TemporaryLocalVariable> unboxMap, Variable v, List<Instr> newInstrs) {
Variable unboxedV = getUnboxedVar(reqdType, unboxMap, v);
if (reqdType == java.lang.Boolean.class) {
newInstrs.add(new UnboxBooleanInstr(unboxedV, v));
} else if (reqdType == Float.class) {
newInstrs.add(new UnboxFloatInstr(unboxedV, v));
} else if (reqdType == Fixnum.class) {
newInstrs.add(new UnboxFixnumInstr(unboxedV, v));
}
state.unboxedVars.put(v, reqdType);
}
private Operand unboxOperand(UnboxState state, Class reqdType, Map<Variable, TemporaryLocalVariable> unboxMap, Operand arg, List<Instr> newInstrs) {
if (arg instanceof Variable) {
Variable v = (Variable)arg;
boolean isUnboxed = state.unboxedVars.get(v) == reqdType;
TemporaryLocalVariable unboxedVar = getUnboxedVar(reqdType, unboxMap, v);
if (!isUnboxed) {
unboxVar(state, reqdType, unboxMap, v, newInstrs);
}
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());
}
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;
}
private void boxRequiredVars(Instr i, UnboxState state, Map<Variable, TemporaryLocalVariable> unboxMap, Variable dst, boolean hasRescuer, boolean isDFBarrier, List<Instr> newInstrs) {
boolean isClosure = problem.getFIC().getScope() instanceof IRClosure;
HashSet<Variable> varsToBox = new HashSet<Variable>();
if (i.canRaiseException()) {
if (hasRescuer) {
varsToBox.addAll(state.unboxedDirtyVars);
} else if (isClosure) {
markLocalVariables(varsToBox, state.unboxedDirtyVars);
}
}
if (isClosure && (i instanceof ReturnInstr || i instanceof BreakInstr)) {
markLocalVariables(varsToBox, state.unboxedDirtyVars);
}
List<Variable> boxedLocalVars = null;
if (isDFBarrier) {
markLocalVariables(varsToBox, state.unboxedDirtyVars);
boxedLocalVars = new ArrayList<Variable>();
markLocalVariables(boxedLocalVars, state.unboxedVars.keySet());
}
Operation op = i.getOperation();
boolean isBranch = op == Operation.B_TRUE || op == Operation.B_FALSE;
if (!isBranch) {
for (Variable v: i.getUsedVariables()) {
if (state.unboxedDirtyVars.contains(v)) {
varsToBox.add(v);
}
}
}
for (Variable v: varsToBox) {
boxVar(state, state.unboxedVars.get(v), unboxMap, v, newInstrs);
}
if (isBranch) {
OneOperandBranchInstr bi = (OneOperandBranchInstr)i;
Operand a = bi.getArg1();
Operand ua = getUnboxedOperand(state, unboxMap, a);
if (ua == a) {
newInstrs.add(i);
} else if (op == Operation.B_TRUE) {
newInstrs.add(new BTrueInstr(bi.getJumpTarget(), ua));
} else {
newInstrs.add(new BFalseInstr(bi.getJumpTarget(), ua));
}
} else {
newInstrs.add(i);
}
if (dst != null) {
state.unboxedVars.remove(dst);
state.unboxedDirtyVars.remove(dst);
}
if (boxedLocalVars != null) {
state.unboxedVars.keySet().removeAll(boxedLocalVars);
}
}
public void unbox(Map<Variable, TemporaryLocalVariable> unboxMap) {
CFG cfg = getCFG();
HashMap<Variable, Class> succUnboxedVars = new HashMap<Variable, Class>();
for (BasicBlock b: cfg.getOutgoingDestinations(basicBlock)) {
if (b.isExitBB()) continue;
Map<Variable, Class> xVars = problem.getFlowGraphNode(b).inState.unboxedVars;
for (Variable v2: xVars.keySet()) {
succUnboxedVars.put(v2, xVars.get(v2));
}
}
for (Variable v3: outState.unboxedVars.keySet()) {
succUnboxedVars.remove(v3);
}
LiveVariablesProblem lvp = (LiveVariablesProblem) problem.getFIC().getDataFlowProblems().get(LiveVariablesProblem.NAME);
BitSet liveVarsSet = lvp.getFlowGraphNode(basicBlock).getLiveInBitSet();
List<Instr> newInstrs = new ArrayList<Instr>();
boolean unboxedLiveVars = false;
initSolution();
for (Instr i : basicBlock.getInstrs()) {
Variable dst = null;
Class dstType = Object.class;
boolean unboxedAndDirty = false;
boolean hitDFBarrier = false;
if (i.getOperation().transfersControl()) {
for (Variable v: succUnboxedVars.keySet()) {
if (liveVarsSet.get(lvp.getDFVar(v))) {
unboxVar(tmpState, succUnboxedVars.get(v), unboxMap, v, newInstrs);
}
}
unboxedLiveVars = true;
} else {
if (i instanceof ResultInstr) {
dst = ((ResultInstr) i).getResult();
}
if (i instanceof CopyInstr) {
Operand src = ((CopyInstr)i).getSource();
Class srcType = getOperandType(tmpState, src);
dstType = srcType;
if (srcType == Float.class || srcType == Fixnum.class) {
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));
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.getId();
Operand r = c.getReceiver();
if (dst != null && c.getArgsCount() == 1 && problem.isUnboxableMethod(m)) {
Operand a = c.getArg1();
Class receiverType = getOperandType(tmpState, r);
Class argType = getOperandType(tmpState, a);
Operation unboxedOp = null;
Class unboxedType = null;
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 = problem.getUnboxedResultType(m, unboxedType);
tmpState.unboxedVars.put(dst, dstType);
TemporaryLocalVariable unboxedDst = getUnboxedVar(dstType, unboxMap, dst);
r = unboxOperand(tmpState, unboxedType, unboxMap, r, newInstrs);
a = unboxOperand(tmpState, unboxedType, unboxMap, a, newInstrs);
newInstrs.add(new AluInstr(unboxedOp, unboxedDst, r, a));
} else if (c.targetRequiresCallersBinding()) {
hitDFBarrier = true;
}
}
} else {
if (o instanceof WrappedIRClosure) {
hitDFBarrier = true;
FullInterpreterContext clfic = ((WrappedIRClosure)o).getClosure().getFullInterpreterContext();
UnboxableOpsAnalysisProblem subProblem = (UnboxableOpsAnalysisProblem) clfic.getDataFlowProblems().get(UnboxableOpsAnalysisProblem.NAME);
UnboxableOpsAnalysisNode exitNode = subProblem.getExitNode();
subProblem.unbox();
tmpState.computeMEETForTypes(exitNode.outState, true);
hitDFBarrier = true;
} else {
hitDFBarrier = true;
}
}
} else {
}
}
setOperandType(tmpState, dst, dstType);
if (unboxedAndDirty) {
tmpState.unboxedDirtyVars.add(dst);
} else {
boxRequiredVars(i, tmpState, unboxMap, dst, hasExceptionsRescued(), hitDFBarrier, newInstrs);
}
}
if (!unboxedLiveVars) {
for (Variable v: succUnboxedVars.keySet()) {
if (liveVarsSet.get(lvp.getDFVar(v))) {
unboxVar(tmpState, succUnboxedVars.get(v), unboxMap, v, newInstrs);
}
}
}
basicBlock.replaceInstrs(newInstrs);
}
@Override
public String toString() {
return "";
}
UnboxState inState;
UnboxState outState;
UnboxState tmpState;
}