private void updateUnboxedVarsInfo(
      Instr i, UnboxState state, Variable dst, boolean hasRescuer, boolean isDFBarrier) {
    HashSet<Variable> varsToBox = new HashSet<Variable>();
    // Special treatment for instructions that can raise exceptions
    if (i.canRaiseException()) {
      if (hasRescuer) {
        // If we are going to be rescued,
        // box all unboxed dirty vars before we execute the instr.
        state.unboxedDirtyVars.clear();
      } else {
        // We are going to exit if an exception is raised.
        // So, only need to bother with dirty live local vars for closures
        if (problem.getScope() instanceof IRClosure) {
          markLocalVariables(varsToBox, state.unboxedDirtyVars);
        }
      }
    }

    if (isDFBarrier) {
      // All dirty unboxed local vars will get reboxed.
      markLocalVariables(varsToBox, state.unboxedDirtyVars);

      // We have to re-unbox local variables as necessary since we don't
      // know how they are going to change once we get past this instruction.
      List<Variable> lvs = new ArrayList<Variable>();
      markLocalVariables(lvs, state.unboxedVars.keySet());

      state.unboxedVars.keySet().removeAll(lvs);
    }

    // Update set of unboxed dirty vars
    state.unboxedDirtyVars.removeAll(varsToBox);

    // FIXME: Also global variables .. see LVA / StoreLocalVar analysis.

    // B_TRUE and B_FALSE have unboxed forms and their operands
    // needn't get boxed back.
    Operation op = i.getOperation();
    if (op != Operation.B_TRUE && op != Operation.B_FALSE) {
      // Vars used by this instruction that only exist in unboxed form
      // will have to get boxed before it is executed
      state.unboxedDirtyVars.removeAll(i.getUsedVariables());
    }

    // If the instruction writes into 'dst', it will be in boxed form.
    if (dst != null) {
      state.unboxedVars.remove(dst);
      state.unboxedDirtyVars.remove(dst);
    }
  }
Example #2
0
  public void setUpUseDefLocalVarMaps() {
    definedLocalVars = new java.util.HashSet<Variable>();
    usedLocalVars = new java.util.HashSet<Variable>();
    for (BasicBlock bb : cfg().getBasicBlocks()) {
      for (Instr i : bb.getInstrs()) {
        for (Variable v : i.getUsedVariables()) {
          if (v instanceof LocalVariable) usedLocalVars.add(v);
        }

        if (i instanceof ResultInstr) {
          Variable v = ((ResultInstr) i).getResult();

          if (v instanceof LocalVariable) definedLocalVars.add(v);
        }
      }
    }

    for (IRClosure cl : getClosures()) {
      cl.setUpUseDefLocalVarMaps();
    }
  }
  @Override
  public void applyTransferFunction(Instr i) {
    IRScope scope = problem.getScope();
    boolean scopeBindingHasEscaped = scope.bindingHasEscaped();

    // Right away, clear the variable defined by this instruction -- it doesn't have to be loaded!
    if (i instanceof ResultInstr) {
      reqdLoads.remove(((ResultInstr) i).getResult());
    }

    // Process closure accepting instrs specially -- these are the sites of binding loads!
    if (i instanceof ClosureAcceptingInstr) {
      Operand o = ((ClosureAcceptingInstr) i).getClosureArg();
      if (o != null && o instanceof WrappedIRClosure) {
        IRClosure cl = ((WrappedIRClosure) o).getClosure();

        // Variables defined in the closure do not need to be loaded anymore at
        // program points before the call, because they will be loaded after the
        // call completes to fetch the latest value.
        //
        // Allocate a new hash-set and modify it to get around ConcurrentModificationException on
        // reqdLoads
        Set<LocalVariable> newReqdLoads = new HashSet<LocalVariable>(reqdLoads);
        for (LocalVariable v : reqdLoads) {
          if (cl.definesLocalVariable(v)) newReqdLoads.remove(v);
        }
        reqdLoads = newReqdLoads;
      }

      // In this case, we are going to blindly load everything -- so, at the call site, pending
      // loads dont carry over!
      if (scopeBindingHasEscaped) {
        reqdLoads.clear();
      } else {
        // All variables not defined in the current scope have to be always loaded
        // because of multi-threading scenarios where some other scope
        // could update this variable concurrently.
        //
        // Allocate a new hash-set and modify it to get around ConcurrentModificationException on
        // reqdLoads
        Set<LocalVariable> newReqdLoads = new HashSet<LocalVariable>(reqdLoads);
        for (LocalVariable v : reqdLoads) {
          if (!scope.definesLocalVariable(v)) newReqdLoads.remove(v);
        }
        reqdLoads = newReqdLoads;
      }
    } else if (scopeBindingHasEscaped && (i.getOperation() == Operation.PUT_GLOBAL_VAR)) {
      // global-var tracing can execute closures set up in previous trace-var calls
      // in which case we would have the 'scopeBindingHasEscaped' flag set to true
      reqdLoads.clear();
    }

    if (i.getOperation() == Operation.BINDING_STORE) {
      LocalVariable lv = ((StoreLocalVarInstr) i).getLocalVar();
      if (!lv.isSelf()) reqdLoads.add(lv);
    } else {
      // The variables used as arguments will need to be loaded
      // %self is local to every scope and never crosses scope boundaries and need not be
      // spilled/refilled
      for (Variable x : i.getUsedVariables()) {
        if (x instanceof LocalVariable && !x.isSelf()) {
          reqdLoads.add((LocalVariable) x);
        }
      }
    }
  }
  public void addLoads(Map<Operand, Operand> varRenameMap) {
    IRScope scope = problem.getScope();
    boolean isEvalScript = scope instanceof IREvalScript;
    boolean scopeBindingHasEscaped = scope.bindingHasEscaped();

    List<Instr> instrs = basicBlock.getInstrs();
    ListIterator<Instr> it = instrs.listIterator(instrs.size());

    initSolution();
    while (it.hasPrevious()) {
      Instr i = it.previous();

      // Right away, clear the variable defined by this instruction -- it doesn't have to be loaded!
      if (i instanceof ResultInstr) reqdLoads.remove(((ResultInstr) i).getResult());

      // Process closure accepting instrs specially -- these are the sites of binding loads!
      if (i instanceof ClosureAcceptingInstr) {
        Operand o = ((ClosureAcceptingInstr) i).getClosureArg();
        if (o != null && o instanceof WrappedIRClosure) {
          IRClosure cl = ((WrappedIRClosure) o).getClosure();

          // Only those variables that are defined in the closure, and are in the required loads set
          // will need to be loaded from the binding after the call!  Rest can wait ..
          //
          // Allocate a new hash-set and modify it to get around ConcurrentModificationException on
          // reqdLoads
          Set<LocalVariable> newReqdLoads = new HashSet<LocalVariable>(reqdLoads);
          it.next();
          for (LocalVariable v : reqdLoads) {
            if (cl.definesLocalVariable(v)) {
              it.add(
                  new LoadLocalVarInstr(scope, getLocalVarReplacement(v, scope, varRenameMap), v));
              it.previous();
              newReqdLoads.remove(v);
            }
          }
          it.previous();
          reqdLoads = newReqdLoads;
        }

        // In this case, we are going to blindly load everything
        if (scopeBindingHasEscaped) {
          it.next();
          for (LocalVariable v : reqdLoads) {
            it.add(new LoadLocalVarInstr(scope, getLocalVarReplacement(v, scope, varRenameMap), v));
            it.previous();
          }
          it.previous();
          reqdLoads.clear();
        } else {
          // All variables not defined in the current scope have to be always loaded
          // because of multi-threading scenarios where some other scope
          // could update this variable concurrently.
          //
          // Allocate a new hash-set and modify it to get around ConcurrentModificationException on
          // reqdLoads
          Set<LocalVariable> newReqdLoads = new HashSet<LocalVariable>(reqdLoads);
          it.next();
          for (LocalVariable v : reqdLoads) {
            if (!scope.definesLocalVariable(v)) {
              it.add(
                  new LoadLocalVarInstr(scope, getLocalVarReplacement(v, scope, varRenameMap), v));
              it.previous();
              newReqdLoads.remove(v);
            }
          }
          it.previous();
          reqdLoads = newReqdLoads;
        }
      } else if (scopeBindingHasEscaped && (i.getOperation() == Operation.PUT_GLOBAL_VAR)) {
        // global-var tracing can execute closures set up in previous trace-var calls
        // in which case we would have the 'scopeBindingHasEscaped' flag set to true
        it.next();
        for (LocalVariable v : reqdLoads) {
          it.add(new LoadLocalVarInstr(scope, getLocalVarReplacement(v, scope, varRenameMap), v));
          it.previous();
        }
        it.previous();
        reqdLoads.clear();
      }

      if (i.getOperation() == Operation.BINDING_STORE) {
        LocalVariable lv = ((StoreLocalVarInstr) i).getLocalVar();
        if (!lv.isSelf()) {
          reqdLoads.add(lv);
          // SSS FIXME: Why is this reqd again?  Document with example
          // Make sure there is a replacement var for all local vars
          getLocalVarReplacement(lv, scope, varRenameMap);
        }
      } else {
        // The variables used as arguments will need to be loaded
        // %self is local to every scope and never crosses scope boundaries and need not be
        // spilled/refilled
        for (Variable v : i.getUsedVariables()) {
          if (!(v instanceof LocalVariable)) continue;

          LocalVariable lv = (LocalVariable) v;
          if (!lv.isSelf()) {
            reqdLoads.add(lv);
            // SSS FIXME: Why is this reqd again?  Document with example
            // Make sure there is a replacement var for all local vars
            getLocalVarReplacement(lv, scope, varRenameMap);
          }
        }
      }
    }

    // Add loads on entry of a rescue block.
    if (basicBlock.isRescueEntry()) {
      for (LocalVariable v : reqdLoads) {
        it.add(new LoadLocalVarInstr(scope, getLocalVarReplacement(v, scope, varRenameMap), v));
      }
    }

    // Load first use of variables in closures
    if (scope instanceof IRClosure && basicBlock.isEntryBB()) {
      // System.out.println("\n[In Entry BB] For CFG " + getCFG() + ":");
      // System.out.println("\t--> Reqd loads   : " +
      // java.util.Arrays.toString(reqdLoads.toArray()));
      for (LocalVariable v : reqdLoads) {
        if (scope.usesLocalVariable(v) || scope.definesLocalVariable(v)) {
          if (isEvalScript
              || !(v instanceof ClosureLocalVariable)
              || (scope != ((ClosureLocalVariable) v).definingScope)) {
            it.add(new LoadLocalVarInstr(scope, getLocalVarReplacement(v, scope, varRenameMap), v));
          }
        }
      }
    }
  }
  private void boxRequiredVars(
      Instr i,
      UnboxState state,
      Map<Variable, TemporaryLocalVariable> unboxMap,
      Variable dst,
      boolean hasRescuer,
      boolean isDFBarrier,
      List<Instr> newInstrs) {
    // Special treatment for instructions that can raise exceptions
    boolean isClosure = this.problem.getScope() instanceof IRClosure;
    HashSet<Variable> varsToBox = new HashSet<Variable>();
    if (i.canRaiseException()) {
      if (hasRescuer) {
        // If we are going to be rescued,
        // box all unboxed dirty vars before we execute the instr
        varsToBox.addAll(state.unboxedDirtyVars);
      } else if (isClosure) {
        // We are going to exit if an exception is raised.
        // So, only need to bother with dirty live local vars for closures
        for (Variable v : state.unboxedDirtyVars) {
          if (v instanceof LocalVariable) {
            varsToBox.add(v);
          }
        }
      }
    }

    if (isClosure && (i instanceof ReturnInstr || i instanceof BreakInstr)) {
      for (Variable v : state.unboxedDirtyVars) {
        if (v instanceof LocalVariable) {
          varsToBox.add(v);
        }
      }
    }

    if (isDFBarrier) {
      // All dirty unboxed (local) vars will get reboxed.
      for (Variable v : state.unboxedDirtyVars) {
        if (v instanceof LocalVariable) {
          varsToBox.add(v);
        }
      }

      // We have to re-unbox local variables as necessary since we don't
      // know how they are going to change once we get past this instruction.
      List<Variable> lvs = new ArrayList<Variable>();
      for (Variable v : state.unboxedVars) {
        if (v instanceof LocalVariable) {
          lvs.add(v);
        }
      }
      state.unboxedVars.removeAll(lvs);
    }

    // B_TRUE and B_FALSE have unboxed forms and their operands
    // needn't get boxed back.
    Operation op = i.getOperation();
    boolean isBranch = op == Operation.B_TRUE || op == Operation.B_FALSE;
    if (!isBranch) {
      // Vars used by this instruction that only exist in unboxed form
      // will have to get boxed before it is executed
      for (Variable v : i.getUsedVariables()) {
        if (state.unboxedDirtyVars.contains(v)) {
          varsToBox.add(v);
        }
      }
    }

    // Add boxing instrs.
    for (Variable v : varsToBox) {
      newInstrs.add(new BoxFloatInstr(v, getUnboxedVar(unboxMap, v)));
      state.unboxedDirtyVars.remove(v);
    }

    // Add 'i' itself
    if (isBranch) {
      OneOperandBranchInstr bi = (OneOperandBranchInstr) i;
      Operand a = bi.getArg1();
      Operand ua = getUnboxedOperand(state.unboxedVars, unboxMap, a, newInstrs, false);
      if (ua == a) {
        newInstrs.add(i);
      } else if (op == Operation.B_TRUE) {
        newInstrs.add(new BTrueInstr(Operation.B_TRUE_UNBOXED, ua, bi.getJumpTarget()));
      } else {
        newInstrs.add(new BFalseInstr(Operation.B_FALSE_UNBOXED, ua, bi.getJumpTarget()));
      }
    } else {
      newInstrs.add(i);
    }

    // If the instruction writes into 'dst', it will be in boxed form.
    if (dst != null) {
      state.unboxedVars.remove(dst);
      state.unboxedDirtyVars.remove(dst);
    }
  }