public void applyTransferFunction(Instr i) { // Rescue node, if any boolean scopeBindingHasEscaped = problem.getScope().bindingHasEscaped(); Variable dst = null; boolean dirtied = false; boolean hitDFBarrier = false; if (i instanceof ResultInstr) { dst = ((ResultInstr) i).getResult(); } if (i instanceof CopyInstr) { // Copies are easy Operand src = ((CopyInstr) i).getSource(); Class srcType = getOperandType(tmpState, src); setOperandType(tmpState, dst, srcType); // If we have an unboxed type for 'src', we can leave this unboxed. // // FIXME: However, if 'src' is a constant, this could unnecessarily // leave 'src' unboxed and lead to a boxing instruction further down // at the use site of 'dst'. This indicates that leaving this unboxed // 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) { dirtied = true; } } else if (i instanceof CallBase) { // Process calls specially -- these are what we want to optimize! CallBase c = (CallBase) i; Operand o = c.getClosureArg(null); if (o == null) { MethAddr m = c.getMethodAddr(); Operand r = c.getReceiver(); Operand[] args = c.getCallArgs(); if (args.length == 1 && m.resemblesALUOp()) { Operand a = args[0]; 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)) { setOperandType(tmpState, dst, Float.class); // 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 if (r instanceof Variable) { tmpState.unboxedVars.add((Variable) r); } if (a instanceof Variable) { tmpState.unboxedVars.add((Variable) a); } dirtied = true; } else { if (receiverType == Fixnum.class && argType == Fixnum.class) { setOperandType(tmpState, dst, Fixnum.class); } else { setOperandType(tmpState, dst, Object.class); } if (c.targetRequiresCallersBinding()) { hitDFBarrier = true; } } } else { setOperandType(tmpState, dst, Object.class); } } else { if (o instanceof WrappedIRClosure) { // Fetch the nested unboxing-analysis problem, creating one if necessary IRClosure cl = ((WrappedIRClosure) o).getClosure(); UnboxableOpsAnalysisProblem subProblem = (UnboxableOpsAnalysisProblem) cl.getDataFlowSolution(DataFlowConstants.UNBOXING); if (subProblem == null) { subProblem = new UnboxableOpsAnalysisProblem(); subProblem.setup(cl); cl.setDataFlowSolution(DataFlowConstants.UNBOXING, subProblem); } UnboxableOpsAnalysisNode exitNode = (UnboxableOpsAnalysisNode) subProblem.getExitNode(); UnboxableOpsAnalysisNode entryNode = (UnboxableOpsAnalysisNode) subProblem.getEntryNode(); // Init it to MEET(state-on-entry, state-on-exit). // The meet is required to account for participation of the closure in a loop. // Ex: f = 0.0; n.times { f += 10; } 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); // Compute solution subProblem.compute_MOP_Solution(); // Update types to MEET(new-state-on-exit, current-state) tmpState.computeMEETForTypes(exitNode.outState, true); // As for unboxed var state, since binding can escape in // arbitrary ways in the general case, assume the worst for now. // If we are guaranteed that the closure binding is not used // outside the closure itself, we can avoid worst-case behavior // and only clear vars that are modified in the closure. hitDFBarrier = true; } else { // Black hole -- cannot analyze hitDFBarrier = true; } } } else { // We dont know how to optimize this instruction. // So, we assume we dont know type of the result. // TOP/class --> BOTTOM setOperandType(tmpState, dst, Object.class); } if (dirtied) { tmpState.unboxedVars.add(dst); tmpState.unboxedDirtyVars.add(dst); } else { // Since the instruction didn't run in unboxed form, // dirty unboxed vars will have to get boxed here. updateUnboxedVarsInfo(i, tmpState, dst, hasExceptionsRescued(), hitDFBarrier); } }
public void unbox(Map<Variable, TemporaryLocalVariable> unboxMap) { // System.out.println("BB : " + basicBlock + " in " + this.problem.getScope().getName()); // System.out.println("-- known types on entry:"); // for (Variable v: inState.types.keySet()) { // if (inState.types.get(v) != Object.class) { // System.out.println(v + "-->" + inState.types.get(v)); // } // } // System.out.print("-- unboxed vars on entry:"); // for (Variable v: inState.unboxedVars) { // System.out.print(" " + v); // } // System.out.println("------"); // System.out.print("-- unboxed vars on exit:"); // for (Variable v: outState.unboxedVars) { // System.out.print(" " + v); // } // System.out.println("------"); // Compute UNION(unboxedVarsIn(all-successors)) - this.unboxedVarsOut // All vars in this new set have to be unboxed on exit from this BB boolean scopeBindingHasEscaped = problem.getScope().bindingHasEscaped(); Set<Variable> succUnboxedVars = new HashSet<Variable>(); CFG cfg = problem.getScope().cfg(); for (Edge e : cfg.getOutgoingEdges(basicBlock)) { BasicBlock b = (BasicBlock) e.getDestination().getData(); if (b != cfg.getExitBB()) { UnboxableOpsAnalysisNode x = (UnboxableOpsAnalysisNode) problem.getFlowGraphNode(b); succUnboxedVars.addAll(x.inState.unboxedVars); } } succUnboxedVars.removeAll(outState.unboxedVars); // Only worry about vars live on exit from the BB LiveVariablesProblem lvp = (LiveVariablesProblem) problem.getScope().getDataFlowSolution(DataFlowConstants.LVP_NAME); BitSet liveVarsSet = ((LiveVariableNode) lvp.getFlowGraphNode(basicBlock)).getLiveInBitSet(); // Rescue node, if any IRScope scope = this.problem.getScope(); boolean isClosure = scope instanceof IRClosure; List<Instr> newInstrs = new ArrayList<Instr>(); boolean unboxedLiveVars = false; initSolution(); for (Instr i : basicBlock.getInstrs()) { Variable dst = null; boolean dirtied = false; boolean hitDFBarrier = false; // System.out.println("ORIG: " + i); if (i.getOperation().transfersControl()) { // Add unboxing instrs. for (Variable v : succUnboxedVars) { if (liveVarsSet.get(lvp.getDFVar(v).getId())) { // System.out.println("suv: UNBOXING for " + v); newInstrs.add(new UnboxFloatInstr(getUnboxedVar(unboxMap, v), v)); tmpState.unboxedVars.add(v); } } unboxedLiveVars = true; } else { if (i instanceof ResultInstr) { dst = ((ResultInstr) i).getResult(); } if (i instanceof CopyInstr) { // Copies are easy Operand src = ((CopyInstr) i).getSource(); Class srcType = getOperandType(tmpState, src); setOperandType(tmpState, dst, srcType); // If we have an unboxed type for 'src', we can leave this unboxed. // // FIXME: However, if 'src' is a constant, this could unnecessarily // leave 'src' unboxed and lead to a boxing instruction further down // at the use site of 'dst'. This indicates that leaving this unboxed // 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) { Operand unboxedSrc = src instanceof Variable ? getUnboxedVar(unboxMap, (Variable) src) : src; TemporaryLocalVariable unboxedDst = getUnboxedVar(unboxMap, dst); newInstrs.add(new CopyInstr(Operation.COPY_UNBOXED, unboxedDst, unboxedSrc)); dirtied = true; } } else if (i instanceof CallBase) { // Process calls specially -- these are what we want to optimize! CallBase c = (CallBase) i; Operand o = c.getClosureArg(null); if (o == null) { MethAddr m = c.getMethodAddr(); Operand r = c.getReceiver(); Operand[] args = c.getCallArgs(); if (args.length == 1 && m.resemblesALUOp()) { Operand a = args[0]; 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)) { setOperandType(tmpState, dst, Float.class); r = getUnboxedOperand(tmpState.unboxedVars, unboxMap, r, newInstrs); a = getUnboxedOperand(tmpState.unboxedVars, unboxMap, a, newInstrs); TemporaryLocalVariable unboxedDst = getUnboxedVar(unboxMap, dst); newInstrs.add(new AluInstr(m.getUnboxedOp(Float.class), unboxedDst, r, a)); dirtied = true; } else { if (receiverType == Fixnum.class && argType == Fixnum.class) { setOperandType(tmpState, dst, Fixnum.class); } else { setOperandType(tmpState, dst, Object.class); } if (c.targetRequiresCallersBinding()) { hitDFBarrier = true; } } } else { setOperandType(tmpState, dst, Object.class); } } else { if (o instanceof WrappedIRClosure) { // Since binding can escape in arbitrary ways in the general case, // assume the worst for now. If we are guaranteed that the closure binding // is not used outside the closure itself, we can avoid worst-case behavior. hitDFBarrier = true; // Fetch the nested unboxing-analysis problem, creating one if necessary IRClosure cl = ((WrappedIRClosure) o).getClosure(); UnboxableOpsAnalysisProblem subProblem = (UnboxableOpsAnalysisProblem) cl.getDataFlowSolution(DataFlowConstants.UNBOXING); UnboxableOpsAnalysisNode exitNode = (UnboxableOpsAnalysisNode) subProblem.getExitNode(); // Compute solution subProblem.unbox(); // Update types to MEET(new-state-on-exit, current-state) tmpState.computeMEETForTypes(exitNode.outState, true); // As for unboxed var state, since binding can escape in // arbitrary ways in the general case, assume the worst for now. // If we are guaranteed that the closure binding is not used // outside the closure itself, we can avoid worst-case behavior // and only clear vars that are modified in the closure. hitDFBarrier = true; } else { // Cannot analyze hitDFBarrier = true; } } } else { // We dont know how to optimize this instruction. // So, we assume we dont know type of the result. // TOP/class --> BOTTOM setOperandType(tmpState, dst, Object.class); } } if (dirtied) { tmpState.unboxedVars.add(dst); tmpState.unboxedDirtyVars.add(dst); } else { // Since the instruction didn't run in unboxed form, // dirty unboxed vars will have to get boxed here. boxRequiredVars( i, tmpState, unboxMap, dst, hasExceptionsRescued(), hitDFBarrier, newInstrs); } } // Add unboxing instrs. if (!unboxedLiveVars) { for (Variable v : succUnboxedVars) { if (liveVarsSet.get(lvp.getDFVar(v).getId())) { // System.out.println("suv: UNBOXING for " + v); newInstrs.add(new UnboxFloatInstr(getUnboxedVar(unboxMap, v), v)); } } } /* System.out.println("------"); for (Instr i : newInstrs) { System.out.println("NEW: " + i); } */ basicBlock.replaceInstrs(newInstrs); }
public void unbox(Map<Variable, TemporaryLocalVariable> unboxMap) { // System.out.println("BB : " + basicBlock + " in " + problem.getScope().getName()); // System.out.println("-- known types on entry:"); // for (Variable v: inState.types.keySet()) { // if (inState.types.get(v) != Object.class) { // System.out.println(v + "-->" + inState.types.get(v)); // } // } // System.out.print("-- unboxed vars on entry:"); // for (Variable v: inState.unboxedVars) { // System.out.print(" " + v); // } // System.out.println("------"); // System.out.print("-- unboxed vars on exit:"); // for (Variable v: outState.unboxedVars) { // System.out.print(" " + v); // } // System.out.println("------"); CFG cfg = getCFG(); // Compute UNION(unboxedVarsIn(all-successors)) - this.unboxedVarsOut // All vars in this new set have to be unboxed on exit from this BB 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()) { // VERY IMPORTANT: Pay attention! // // Technically, the successors of this node may not all agree on what // the unboxed type ought to be for 'v2'. For example, one successor might // want 'v2' in Fixnum form and other might want it in Float form. If that // happens, we have to add unboxing instructions for each of those expected // types. However, for now, we are going to punt and assume that our successors // agree on unboxed types for 'v2'. succUnboxedVars.put(v2, xVars.get(v2)); } } // Same caveat as above applies here for (Variable v3 : outState.unboxedVars.keySet()) { succUnboxedVars.remove(v3); } // Only worry about vars live on exit from the BB LiveVariablesProblem lvp = (LiveVariablesProblem) problem.getScope().getDataFlowSolution(DataFlowConstants.LVP_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; boolean dirtied = false; boolean hitDFBarrier = false; // System.out.println("ORIG: " + i); if (i.getOperation().transfersControl()) { // Add unboxing instrs. 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) { // Copies are easy Operand src = ((CopyInstr) i).getSource(); Class srcType = getOperandType(tmpState, src); setOperandType(tmpState, dst, srcType); // If we have an unboxed type for 'src', we can leave this unboxed. // // FIXME: However, if 'src' is a constant, this could unnecessarily // leave 'src' unboxed and lead to a boxing instruction further down // at the use site of 'dst'. This indicates that leaving this unboxed // 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 || 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)); tmpState.unboxedVars.put(dst, srcType); dirtied = true; } } else if (i instanceof ClosureAcceptingInstr) { Operand o = ((ClosureAcceptingInstr) i).getClosureArg(); if (i instanceof CallBase && o == null) { CallBase c = (CallBase) i; MethAddr m = c.getMethodAddr(); Operand r = c.getReceiver(); Operand[] args = c.getCallArgs(); if (dst != null && args.length == 1 && m.resemblesALUOp()) { Operand a = args[0]; Class receiverType = getOperandType(tmpState, r); Class argType = getOperandType(tmpState, a); // Optimistically assume that call is an ALU op Operation unboxedOp; if ((receiverType == Float.class || (receiverType == Fixnum.class && argType == Float.class)) && (unboxedOp = m.getUnboxedOp(Float.class)) != null) { dirtied = true; Class dstType = m.getUnboxedResultType(Float.class); setOperandType(tmpState, dst, dstType); tmpState.unboxedVars.put(dst, dstType); TemporaryLocalVariable unboxedDst = getUnboxedVar(dstType, unboxMap, dst); r = unboxOperand(tmpState, Float.class, unboxMap, r, newInstrs); a = unboxOperand(tmpState, Float.class, unboxMap, a, newInstrs); newInstrs.add(new AluInstr(unboxedOp, unboxedDst, r, a)); } else if ((receiverType == Float.class || (receiverType == Fixnum.class && argType == Fixnum.class)) && (unboxedOp = m.getUnboxedOp(Fixnum.class)) != null) { dirtied = true; Class dstType = m.getUnboxedResultType(Fixnum.class); setOperandType(tmpState, dst, dstType); tmpState.unboxedVars.put(dst, dstType); TemporaryLocalVariable unboxedDst = getUnboxedVar(dstType, unboxMap, dst); r = unboxOperand(tmpState, Fixnum.class, unboxMap, r, newInstrs); a = unboxOperand(tmpState, Fixnum.class, unboxMap, a, newInstrs); newInstrs.add(new AluInstr(unboxedOp, unboxedDst, r, a)); } else { if (receiverType == Fixnum.class && argType == Fixnum.class) { setOperandType(tmpState, dst, Fixnum.class); } else { setOperandType(tmpState, dst, Object.class); } if (c.targetRequiresCallersBinding()) { hitDFBarrier = true; } } } else { setOperandType(tmpState, dst, Object.class); } } else { if (o instanceof WrappedIRClosure) { // Since binding can escape in arbitrary ways in the general case, // assume the worst for now. If we are guaranteed that the closure binding // is not used outside the closure itself, we can avoid worst-case behavior. hitDFBarrier = true; // Fetch the nested unboxing-analysis problem, creating one if necessary IRClosure cl = ((WrappedIRClosure) o).getClosure(); UnboxableOpsAnalysisProblem subProblem = (UnboxableOpsAnalysisProblem) cl.getDataFlowSolution(DataFlowConstants.UNBOXING); UnboxableOpsAnalysisNode exitNode = subProblem.getExitNode(); // Compute solution subProblem.unbox(); // Update types to MEET(new-state-on-exit, current-state) tmpState.computeMEETForTypes(exitNode.outState, true); // As for unboxed var state, since binding can escape in // arbitrary ways in the general case, assume the worst for now. // If we are guaranteed that the closure binding is not used // outside the closure itself, we can avoid worst-case behavior // and only clear vars that are modified in the closure. hitDFBarrier = true; } else { // Cannot analyze hitDFBarrier = true; } } } else { // We dont know how to optimize this instruction. // So, we assume we dont know type of the result. // TOP/class --> BOTTOM setOperandType(tmpState, dst, Object.class); } } if (dirtied) { tmpState.unboxedDirtyVars.add(dst); } else { // Since the instruction didn't run in unboxed form, // dirty unboxed vars will have to get boxed here. boxRequiredVars( i, tmpState, unboxMap, dst, hasExceptionsRescued(), hitDFBarrier, newInstrs); } } // Add unboxing instrs. if (!unboxedLiveVars) { for (Variable v : succUnboxedVars.keySet()) { if (liveVarsSet.get(lvp.getDFVar(v))) { unboxVar(tmpState, succUnboxedVars.get(v), unboxMap, v, newInstrs); } } } /* System.out.println("------"); for (Instr i : newInstrs) { System.out.println("NEW: " + i); } */ basicBlock.replaceInstrs(newInstrs); }