text: /*
     +----------------------------------------------------------------------+
     | HipHop for PHP                                                       |
     +----------------------------------------------------------------------+
     | Copyright (c) 2010-2016 Facebook, Inc. (http://www.facebook.com)     |
     +----------------------------------------------------------------------+
     | This source file is subject to version 3.01 of the PHP license,      |
     | that is bundled with this package in the file LICENSE, and is        |
     | available through the world-wide-web at the following url:           |
     | http://www.php.net/license/3_01.txt                                  |
     | If you did not receive a copy of the PHP license and are unable to   |
     | obtain it through the world-wide-web, please send a note to          |
     | license@php.net so we can mail you a copy immediately.               |
     +----------------------------------------------------------------------+
  */
  
  #include "hphp/runtime/vm/jit/reg-alloc.h"
  
  #include "hphp/runtime/base/arch.h"
  
  #include "hphp/runtime/vm/jit/abi.h"
  #include "hphp/runtime/vm/jit/abi-arm.h"
  #include "hphp/runtime/vm/jit/irlower.h"
  #include "hphp/runtime/vm/jit/minstr-effects.h"
  #include "hphp/runtime/vm/jit/native-calls.h"
  #include "hphp/runtime/vm/jit/print.h"
  #include "hphp/runtime/vm/jit/vasm-instr.h"
  #include "hphp/runtime/vm/jit/vasm-unit.h"
  #include "hphp/runtime/vm/jit/vasm-util.h"
  
  #include <boost/dynamic_bitset.hpp>
  
  namespace HPHP { namespace jit {
  
  using namespace jit::reg;
  using NativeCalls::CallMap;
  
  TRACE_SET_MOD(hhir);
  
  //////////////////////////////////////////////////////////////////////
  
  namespace {
  
  /*
   * Return true if this instruction can load a TypedValue using a 16-byte load
   * into a SIMD register.
   */
  bool loadsCell(Opcode op) {
    switch (op) {
    case LdStk:
    case LdLoc:
    case LdMem:
    case LdContField:
    case LdElem:
    case LdRef:
    case LdStaticLocCached:
    case LookupCns:
    case LookupClsCns:
    case CGetProp:
    case VGetProp:
    case ArrayGet:
    case MapGet:
    case CGetElem:
    case VGetElem:
    case ArrayIdx:
    case GenericIdx:
      switch (arch()) {
      case Arch::X64: return true;
      case Arch::ARM: return false;
      case Arch::PPC64: not_implemented(); break;
      }
      not_reached();
  
    default:
      return false;
    }
  }
  
  /*
   * Returns true if the instruction can store source operand srcIdx to
   * memory as a cell using a 16-byte store.  (implying its okay to
   * clobber TypedValue.m_aux)
   */
  bool storesCell(const IRInstruction& inst, uint32_t srcIdx) {
    switch (arch()) {
    case Arch::X64: break;
    case Arch::ARM: return false;
    case Arch::PPC64: not_implemented(); break;
    }
  
    // If this function returns true for an operand, then the register allocator
    // may give it an XMM register, and the instruction will store the whole 16
    // bytes into memory.  Therefore it's important *not* to return true if the
    // TypedValue.m_aux field in memory has important data.  This is the case for
    // MixedArray elements, Map elements, and RefData inner values.  We don't
    // have StMem in here since it sometimes stores to RefDatas.
    switch (inst.op()) {
    case StRetVal:
      if (!inst.extra<StRetValData>()->wide) return false;
      // fall through
    case StLoc:
      return srcIdx == 1;
  
    case StElem:
      return srcIdx == 2;
  
    case StStk:
      return srcIdx == 1;
  
    default:
      return false;
    }
  }
  
  }
  
  //////////////////////////////////////////////////////////////////////
  
  PhysReg forceAlloc(const SSATmp& tmp) {
    if (tmp.type() <= TBottom) return InvalidReg;
  
    auto inst = tmp.inst();
    auto opc = inst->op();
  
    auto const forceStkPtrs = [&] {
      switch (arch()) {
      case Arch::X64: return false;
      case Arch::ARM: return true;
      case Arch::PPC64: not_implemented(); break;
      }
      not_reached();
    }();
  
    if (forceStkPtrs && tmp.isA(TStkPtr)) {
      assert_flog(
        opc == DefSP ||
        opc == Mov,
        "unexpected StkPtr dest from {}",
        opcodeName(opc)
      );
      return rvmsp();
    }
  
    // LdContActRec and LdAFWHActRec, loading a generator's AR, is the only time
    // we have a pointer to an AR that is not in rvmfp().
    if (opc != LdContActRec && opc != LdAFWHActRec && tmp.isA(TFramePtr)) {
      return rvmfp();
    }
  
    return InvalidReg;
  }
  
  // Assign virtual registers to all SSATmps used or defined in reachable
  // blocks. This assigns a value register to constants defined by DefConst,
  // because some HHIR instructions require them. Ordinary Gen values with
  // a known DataType only get one register. Assign "wide" locations when
  // possible (when all uses and defs can be wide). These will be assigned
  // SIMD registers later.
  void assignRegs(const IRUnit& unit, Vunit& vunit, irlower::IRLS& state,
                  const BlockList& blocks) {
    // visit instructions to find tmps eligible to use SIMD registers
    auto const try_wide = RuntimeOption::EvalHHIRAllocSIMDRegs;
    boost::dynamic_bitset<> not_wide(unit.numTmps());
    StateVector<SSATmp,SSATmp*> tmps(unit, nullptr);
    for (auto block : blocks) {
      for (auto& inst : *block) {
        for (uint32_t i = 0, n = inst.numSrcs(); i < n; i++) {
          auto s = inst.src(i);
          tmps[s] = s;
          if (!try_wide || !storesCell(inst, i)) {
            not_wide.set(s->id());
          }
        }
        for (auto& d : inst.dsts()) {
          tmps[d] = d;
          if (!try_wide || inst.isControlFlow() || !loadsCell(inst.op())) {
            not_wide.set(d->id());
          }
        }
      }
    }
    // visit each tmp, assign 1 or 2 registers to each.
    for (auto tmp : tmps) {
      if (!tmp) continue;
      auto forced = forceAlloc(*tmp);
      if (forced != InvalidReg) {
        state.locs[tmp] = Vloc{forced};
        UNUSED Reg64 r = forced;
        FTRACE(kRegAllocLevel, "force t{} in {}\n", tmp->id(), reg::regname(r));
        continue;
      }
      if (tmp->inst()->is(DefConst)) {
        auto const c = make_const(vunit, tmp->type());
        state.locs[tmp] = Vloc{c};
        FTRACE(kRegAllocLevel, "const t{} in %{}\n", tmp->id(), size_t(c));
      } else {
        if (tmp->numWords() == 2) {
          if (!not_wide.test(tmp->id())) {
            auto r = vunit.makeReg();
            state.locs[tmp] = Vloc{Vloc::kWide, r};
            FTRACE(kRegAllocLevel, "def t{} in wide %{}\n", tmp->id(), size_t(r));
          } else {
            auto data = vunit.makeReg();
            auto type = vunit.makeReg();
            state.locs[tmp] = Vloc{data, type};
            FTRACE(kRegAllocLevel, "def t{} in %{},%{}\n", tmp->id(),
                   size_t(data), size_t(type));
          }
        } else {
          auto data = vunit.makeReg();
          state.locs[tmp] = Vloc{data};
          FTRACE(kRegAllocLevel, "def t{} in %{}\n", tmp->id(), size_t(data));
        }
      }
    }
  }
  
  void getEffects(const Abi& abi, const Vinstr& i,
                  RegSet& uses, RegSet& across, RegSet& defs) {
    uses = defs = across = RegSet();
    switch (i.op) {
      case Vinstr::call:
      case Vinstr::callm:
      case Vinstr::callr:
      case Vinstr::calls:
      case Vinstr::callstub:
        defs = abi.all() - (abi.calleeSaved | rvmfp());
  
        switch (arch()) {
          case Arch::ARM: defs |= PhysReg(arm::rLinkReg); break;
          case Arch::X64: break;
          case Arch::PPC64: not_implemented(); break;
        }
        break;
  
      case Vinstr::callfaststub:
        defs = abi.all() - abi.calleeSaved - abi.gpUnreserved;
        break;
  
      case Vinstr::callphp:
        defs = abi.all();
        switch (arch()) {
          case Arch::ARM: break;
          case Arch::X64: defs -= rvmtl(); break;
          case Arch::PPC64: not_implemented(); break;
        }
        break;
  
      case Vinstr::callarray:
      case Vinstr::contenter:
        defs = abi.all() - RegSet(rvmfp());
        switch (arch()) {
          case Arch::ARM: break;
          case Arch::X64: defs -= rvmtl(); break;
          case Arch::PPC64: not_implemented(); break;
        }
        break;
  
      case Vinstr::cqo:
        uses = RegSet(reg::rax);
        defs = reg::rax | reg::rdx;
        break;
      case Vinstr::idiv:
        uses = defs = reg::rax | reg::rdx;
        break;
      case Vinstr::shlq:
      case Vinstr::sarq:
        across = RegSet(reg::rcx);
        break;
  
      // arm instrs
      case Vinstr::hostcall:
        defs = (abi.all() - abi.calleeSaved) |
               RegSet(PhysReg(arm::rHostCallReg));
        break;
  
      case Vinstr::vcall:
      case Vinstr::vinvoke:
      case Vinstr::vcallarray:
        always_assert(false && "Unsupported instruction in vxls");
  
      default:
        break;
    }
  }
  
  //////////////////////////////////////////////////////////////////////
  
  }}
  