Exemplo n.º 1
0
  //
  // This can help use eliminate writes to %block that are not used since this is
  // a special local-variable, not programmer-defined local-variable
  public void computeScopeFlags() {
    if (flagsComputed) {
      return;
    }

    // init
    canModifyCode = true;
    canCaptureCallersBinding = false;
    usesZSuper = false;
    usesEval = false;
    usesBackrefOrLastline = false;
    // NOTE: bindingHasEscaped is the crucial flag and it effectively is
    // unconditionally true whenever it has a call that receives a closure.
    // See CallInstr.computeRequiresCallersBindingFlag
    bindingHasEscaped =
        (this instanceof IREvalScript); // for eval scopes, bindings are considered escaped ...
    hasBreakInstrs = false;
    hasNonlocalReturns = false;
    canReceiveBreaks = false;
    canReceiveNonlocalReturns = false;

    // recompute flags -- we could be calling this method different times
    // definitely once after ir generation and local optimizations propagates constants locally
    // but potentially at a later time after doing ssa generation and constant propagation
    if (cfg == null) {
      computeScopeFlags(false, getInstrs());
    } else {
      boolean receivesClosureArg = false;
      for (BasicBlock b : cfg.getBasicBlocks()) {
        receivesClosureArg = computeScopeFlags(receivesClosureArg, b.getInstrs());
      }
    }

    // Compute flags for nested closures (recursively) and set derived flags.
    for (IRClosure cl : getClosures()) {
      cl.computeScopeFlags();
      if (cl.hasBreakInstrs || cl.canReceiveBreaks) {
        canReceiveBreaks = true;
      }
      if (cl.hasNonlocalReturns || cl.canReceiveNonlocalReturns) {
        canReceiveNonlocalReturns = true;
      }
      if (cl.usesZSuper()) {
        usesZSuper = true;
      }
    }

    flagsComputed = true;
  }
  @Override
  public Object execute(IRScope scope, Object... data) {
    // IRScriptBody do not get explicit call protocol instructions right now.
    // They dont push/pop a frame and do other special things like run begin/end blocks.
    // So, for now, they go through the runtime stub in IRScriptBody.
    //
    // Add explicit frame and binding push/pop instrs ONLY for methods -- we cannot handle this in
    // closures and evals yet
    // If the scope uses $_ or $~ family of vars, has local load/stores, or if its binding has
    // escaped, we have
    // to allocate a dynamic scope for it and add binding push/pop instructions.
    if (explicitCallProtocolSupported(scope)) {
      StoreLocalVarPlacementProblem slvpp =
          (StoreLocalVarPlacementProblem)
              scope.getDataFlowSolution(StoreLocalVarPlacementProblem.NAME);
      boolean scopeHasLocalVarStores = false;
      boolean bindingHasEscaped = scope.bindingHasEscaped();

      CFG cfg = scope.cfg();

      if (slvpp != null && bindingHasEscaped) {
        scopeHasLocalVarStores = slvpp.scopeHasLocalVarStores();
      } else {
        // We dont require local-var load/stores to have been run.
        // If it is not run, we go conservative and add push/pop binding instrs. everywhere
        scopeHasLocalVarStores = bindingHasEscaped;
      }

      boolean requireFrame = doesItRequireFrame(scope, bindingHasEscaped);
      boolean requireBinding = !scope.getFlags().contains(IRFlags.DYNSCOPE_ELIMINATED);

      if (requireBinding || requireFrame) {
        BasicBlock entryBB = cfg.getEntryBB();
        // Push
        if (requireFrame) entryBB.addInstr(new PushFrameInstr(scope.getName()));
        if (requireBinding) entryBB.addInstr(new PushBindingInstr());

        // SSS FIXME: We are doing this conservatively.
        // Only scopes that have unrescued exceptions need a GEB.
        //
        // Allocate GEB if necessary for popping
        BasicBlock geb = cfg.getGlobalEnsureBB();
        if (geb == null) {
          Variable exc = scope.createTemporaryVariable();
          geb = new BasicBlock(cfg, Label.getGlobalEnsureBlockLabel());
          geb.addInstr(
              new ReceiveJRubyExceptionInstr(exc)); // JRuby Implementation exception handling
          geb.addInstr(new ThrowExceptionInstr(exc));
          cfg.addGlobalEnsureBB(geb);
        }

        // Pop on all scope-exit paths
        for (BasicBlock bb : cfg.getBasicBlocks()) {
          Instr i = null;
          ListIterator<Instr> instrs = bb.getInstrs().listIterator();
          while (instrs.hasNext()) {
            i = instrs.next();
            // Right now, we only support explicit call protocol on methods.
            // So, non-local returns and breaks don't get here.
            // Non-local-returns and breaks are tricky since they almost always
            // throw an exception and we don't multiple pops (once before the
            // return/break, and once when the exception is caught).
            if (!bb.isExitBB() && i instanceof ReturnBase) {
              // Add before the break/return
              instrs.previous();
              if (requireBinding) instrs.add(new PopBindingInstr());
              if (requireFrame) instrs.add(new PopFrameInstr());
              break;
            }
          }

          if (bb.isExitBB() && !bb.isEmpty()) {
            // Last instr could be a return -- so, move iterator one position back
            if (i != null && i instanceof ReturnBase) instrs.previous();
            if (requireBinding) instrs.add(new PopBindingInstr());
            if (requireFrame) instrs.add(new PopFrameInstr());
          }

          if (bb == geb) {
            // Add before throw-exception-instr which would be the last instr
            if (i != null) {
              // Assumption: Last instr should always be a control-transfer instruction
              assert i.getOperation().transfersControl()
                  : "Last instruction of GEB in scope: "
                      + scope
                      + " is "
                      + i
                      + ", not a control-xfer instruction";
              instrs.previous();
            }
            if (requireBinding) instrs.add(new PopBindingInstr());
            if (requireFrame) instrs.add(new PopFrameInstr());
          }
        }
      }

      // This scope has an explicit call protocol flag now
      scope.setExplicitCallProtocolFlag();
    }

    // FIXME: Useless for now
    // Run on all nested closures.
    for (IRClosure c : scope.getClosures()) run(c, false, true);

    // LVA information is no longer valid after the pass
    // FIXME: Grrr ... this seems broken to have to create a new object to invalidate
    (new LiveVariableAnalysis()).invalidate(scope);

    return null;
  }