Exemplo n.º 1
0
// Return the set of physical registers implicitly accessed (used or defined)
// TODO: t4779515: replace this, and other switches, with logic using
// attributes, instead of hardcoded opcode names.
void getEffects(const Abi& abi, const Vinstr& i, RegSet& uses, RegSet& defs) {
  uses = defs = RegSet();
  switch (i.op) {
    case Vinstr::mccall:
    case Vinstr::call:
    case Vinstr::callm:
    case Vinstr::callr:
      defs = abi.all() - abi.calleeSaved;
      break;
    case Vinstr::callstub:
      defs = i.callstub_.kills;
      break;
    case Vinstr::bindcall:
    case Vinstr::contenter:
      defs = abi.all();
      break;
    case Vinstr::cqo:
      uses = RegSet(rax);
      defs = RegSet(rdx);
      break;
    case Vinstr::idiv:
      uses = defs = RegSet(rax).add(rdx);
      break;
    case Vinstr::shlq:
    case Vinstr::sarq:
      uses = RegSet(rcx);
      break;
    default:
      break;
  }
}
Exemplo n.º 2
0
  static RegSet range(Register start, Register end) {
    uint32_t bits = ~0;
    bits <<= start->encoding();
    bits <<= 31 - end->encoding();
    bits >>= 31 - end->encoding();

    return RegSet(bits);
  }
Exemplo n.º 3
0
RegSet
RegAlloc::getRegsLike(RegInfo::State state) const {
  RegSet retval;
  FOR_EACH_REG(r) {
    if (r->m_state == state) {
      retval |= RegSet(r->m_pReg);
    }
  }
  return retval;
}
Exemplo n.º 4
0
TCA emitCallToExit(CodeBlock& cb, DataBlock& data, const UniqueStubs& us) {
  ppc64_asm::Assembler a { cb };
  auto const start = a.frontier();

  if (RuntimeOption::EvalHHIRGenerateAsserts) {
    vwrap(cb, data, [&] (Vout& v) {
      // Not doing it directly as rret(0) == rarg(0) on ppc64
      Vreg ret_addr = v.makeReg();

      // exittc address pushed on calltc/resumetc.
      v << copy{rsp(), ret_addr};

      // We need to spill the return registers around the assert call.
      v << push{rret(0)};
      v << push{rret(1)};

      v << copy{ret_addr, rarg(0)};
      v << call{TCA(assert_tc_saved_rip), RegSet(rarg(0))};

      v << pop{rret(1)};
      v << pop{rret(0)};
    });
  }

  // Discard the exittc address pushed on calltc/resumetc for balancing the
  // stack next.
  a.addi(rsp(), rsp(), 8);

  // Reinitialize r1 for the external code found after enterTCExit's stubret
  a.addi(rsfp(), rsp(), 8);

  // r31 should have the same value as caller's r1. Loading it soon on stubret.
  // (this corrupts the backchain, but it's not relevant as this frame will be
  // destroyed soon)
  a.std(rsfp(), rsp()[8]);

  // Emulate a ret to enterTCExit without actually doing one to avoid
  // unbalancing the return stack buffer.
  a.branchAuto(TCA(mcg->ustubs().enterTCExit));
  return start;
}
Exemplo n.º 5
0
TCA emitCallToExit(CodeBlock& cb, DataBlock& data, const UniqueStubs& /*us*/) {
  ppc64_asm::Assembler a { cb };
  auto const start = a.frontier();

  if (RuntimeOption::EvalHHIRGenerateAsserts) {
    vwrap(cb, data, [&] (Vout& v) {
      // Not doing it directly as rret(0) == rarg(0) on ppc64
      Vreg ret_addr = v.makeReg();

      // exittc address pushed on calltc/resumetc.
      v << copy{rsp(), ret_addr};

      // We need to spill the return registers around the assert call.
      v << push{rret(0)};
      v << push{rret(1)};

      v << copy{ret_addr, rarg(0)};
      v << call{TCA(assert_tc_saved_rip), RegSet(rarg(0))};

      v << pop{rret(1)};
      v << pop{rret(0)};
    });
  }

  // Discard the exittc address pushed on calltc/resumetc for balancing the
  // stack next.
  a.addi(rsp(), rsp(), 8);

  // Reinitialize r1 for the external code found after enterTCExit's stubret
  a.addi(rsfp(), rsp(), 8);

  // Restore the rvmfp when leaving the VM, which must be the same of rsfp.
  a.mr(rvmfp(), rsfp());

  // Emulate a ret to enterTCExit without actually doing one to avoid
  // unbalancing the return stack buffer.
  a.branchAuto(TCA(tc::ustubs().enterTCExit));
  return start;
}
Exemplo n.º 6
0
 static RegSet of(Register r1) {
   return RegSet(r1);
 }
Exemplo n.º 7
0
void RegAlloc::reconcile(RegAlloc& branch) {
  RegSet unconsideredRegs;

  TRACE(1, "Beginning reconcile with a branch\n");

  FOR_EACH_REG (r) {
    if (r->m_state == RegInfo::FREE || r->m_state == RegInfo::SCRATCH) {
      unconsideredRegs |= RegSet(r->m_pReg);
      continue;
    }

    const ContToRegMap::iterator it = branch.m_contToRegMap.find(r->m_cont);
    const bool inSameReg = it != branch.m_contToRegMap.end() &&
                           it->second == r->m_pReg;
    if (inSameReg) {
      if (r->m_state == branch.getInfo(r->m_pReg)->m_state) {
        // Done. Same state, same content, same register. We're cool.
        continue;
      }
    }

    /*
     * If we got past the above, we now have one of two situations.
     *
     *  A) The branch's register is mapped to the same location, but
     *     the states are different between the branch and the main
     *     line.  We skipped FREE and SCRATCH above, so:
     *
     *      - If the branch version is DIRTY, that means the mainline
     *        is CLEAN, so we need to spill now or it will never be
     *        spilt.
     *
     *      - If the branch version is CLEAN, that means the main line
     *        thinks the register is DIRTY---nothing wrong will happen
     *        but we could spill unnecessarily if the branch was
     *        taken.
     *
     *  B) The branch's register is mapped to a possibly different
     *     location (or completely unmapped).
     *
     *      - We still need to clean the register if it is DIRTY, to
     *        evict whatever it holds.
     *
     *      - We then need to fill it with what is actually supposed
     *        to be there.  If it is also DIRTY in the main line
     *        that's ok: we'll just spill more than we needed to if we
     *        took the branch.
     */

    // In both situation A and B we need to spill a DIRTY branch reg.
    if (branch.regIsDirty(r->m_pReg)) {
      branch.cleanReg(r->m_pReg);
    }

    // In situation B (i.e. !inSameReg), we also need to fill the reg
    // with the main line's expected content.
    if (!inSameReg) {
      /*
       * The register allocator has an invariant that only one
       * register can be mapped to a given RegContent at a time.  So
       * if we're going to steal this content from another register
       * via fillByMov, we need to unmap the RegContent from it first.
       * If the register was dirty, we have to spill (note that it's
       * important to do this before freeing r->m_pReg so spill()
       * can't allocate it for an immediate register).
       *
       * XXX: the above comment is out of date (spill can't allocate
       * anymore).
       *
       * If the other register was dirty, we know we'll need to clean
       * it, because the location is obviously in a different
       * register.  (The only case we don't have to spill a dirty
       * branch register is when it is the same state and location in
       * the main line.)
       *
       * Note: we could do better in the case where two register just
       * have swapped locations (this can happen via shuffleRegisters
       * when making a call).
       */
      PhysReg oldReg = InvalidReg;
      if (it != branch.m_contToRegMap.end()) {
        oldReg = it->second;
        if (branch.regIsDirty(oldReg)) {
          branch.cleanReg(oldReg);
        }
        branch.freeRegInfo(branch.physRegToInfo(oldReg));
      }

      branch.freeRegInfo(branch.physRegToInfo(r->m_pReg));
      branch.assignRegInfo(branch.physRegToInfo(r->m_pReg),
                           r->m_cont,
                           RegInfo::CLEAN,
                           r->m_type);
      branch.verify();

      if (r->m_cont.m_kind == RegContent::Int ||
          r->m_cont.m_loc.isLiteral()) {
        ASSERT(r->m_state == RegInfo::CLEAN);
        branch.m_spf->loadImm(r->m_cont.m_int, r->m_pReg);
      } else if (oldReg != InvalidReg) {
        branch.m_spf->fillByMov(oldReg, r->m_pReg);
      } else {
        branch.m_spf->fill(r->m_cont.m_loc, r->m_pReg);
      }
    }
  }

  FOR_EACH_REG_IN_SET (r, unconsideredRegs) {
    if (branch.regIsDirty(r->m_pReg)) {
      branch.cleanReg(r->m_pReg);
    }
  }

  TRACE(1, "Done with branch reconcile\n");
}
Exemplo n.º 8
0
TCA emitFunctionEnterHelper(CodeBlock& cb, UniqueStubs& us) {
  alignJmpTarget(cb);

  auto const start = vwrap(cb, [&] (Vout& v) {
    auto const ar = v.makeReg();

    v << copy{rvmfp(), ar};

    // Fully set up the call frame for the stub.  We can't skip this like we do
    // in other stubs because we need the return IP for this frame in the %rbp
    // chain, in order to find the proper fixup for the VMRegAnchor in the
    // intercept handler.
    v << stublogue{true};
    v << copy{rsp(), rvmfp()};

    // When we call the event hook, it might tell us to skip the callee
    // (because of fb_intercept).  If that happens, we need to return to the
    // caller, but the handler will have already popped the callee's frame.
    // So, we need to save these values for later.
    v << pushm{ar[AROFF(m_savedRip)]};
    v << pushm{ar[AROFF(m_sfp)]};

    v << copy2{ar, v.cns(EventHook::NormalFunc), rarg(0), rarg(1)};

    bool (*hook)(const ActRec*, int) = &EventHook::onFunctionCall;
    v << call{TCA(hook)};
  });

  us.functionEnterHelperReturn = vwrap2(cb, [&] (Vout& v, Vout& vcold) {
    auto const sf = v.makeReg();
    v << testb{rret(), rret(), sf};

    unlikelyIfThen(v, vcold, CC_Z, sf, [&] (Vout& v) {
      auto const saved_rip = v.makeReg();

      // The event hook has already cleaned up the stack and popped the
      // callee's frame, so we're ready to continue from the original call
      // site.  We just need to grab the fp/rip of the original frame that we
      // saved earlier, and sync rvmsp().
      v << pop{rvmfp()};
      v << pop{saved_rip};

      // Drop our call frame; the stublogue{} instruction guarantees that this
      // is exactly 16 bytes.
      v << lea{rsp()[16], rsp()};

      // Sync vmsp and return to the caller.  This unbalances the return stack
      // buffer, but if we're intercepting, we probably don't care.
      v << load{rvmtl()[rds::kVmspOff], rvmsp()};
      v << jmpr{saved_rip};
    });

    // Skip past the stuff we saved for the intercept case.
    v << lea{rsp()[16], rsp()};

    // Restore rvmfp() and return to the callee's func prologue.
    v << stubret{RegSet(), true};
  });

  return start;
}