text: /*
     +----------------------------------------------------------------------+
     | HipHop for PHP                                                       |
     +----------------------------------------------------------------------+
     | Copyright (c) 2010-2014 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.               |
     +----------------------------------------------------------------------+
  */
  
  #ifndef incl_HPHP_VM_CODEGENHELPERS_X64_H_
  #define incl_HPHP_VM_CODEGENHELPERS_X64_H_
  
  #include "hphp/util/asm-x64.h"
  #include "hphp/util/ringbuffer.h"
  
  #include "hphp/runtime/base/types.h"
  #include "hphp/runtime/vm/jit/abi-x64.h"
  #include "hphp/runtime/vm/jit/code-gen-helpers.h"
  #include "hphp/runtime/vm/jit/cpp-call.h"
  #include "hphp/runtime/vm/jit/ir-opcode.h"
  #include "hphp/runtime/vm/jit/phys-reg.h"
  #include "hphp/runtime/vm/jit/service-requests-x64.h"
  #include "hphp/runtime/vm/jit/service-requests.h"
  #include "hphp/runtime/vm/jit/translator.h"
  #include "hphp/runtime/vm/jit/vasm-emit.h"
  #include "hphp/runtime/vm/jit/vasm-instr.h"
  #include "hphp/runtime/vm/jit/vasm-reg.h"
  
  namespace HPHP {
  //////////////////////////////////////////////////////////////////////
  
  struct Func;
  
  namespace jit {
  //////////////////////////////////////////////////////////////////////
  
  struct Fixup;
  struct SSATmp;
  
  namespace x64 {
  //////////////////////////////////////////////////////////////////////
  
  typedef X64Assembler Asm;
  
  constexpr size_t kJmpTargetAlign = 16;
  
  void moveToAlign(CodeBlock& cb, size_t alignment = kJmpTargetAlign);
  
  void emitEagerSyncPoint(Vout& v, const Op* pc, Vreg rds, Vreg vmfp, Vreg vmsp);
  void emitEagerVMRegSave(Asm& as, PhysReg rds, RegSaveFlags flags);
  void emitEagerVMRegSave(Vout& as, Vreg rds, RegSaveFlags flags);
  void emitGetGContext(Asm& as, PhysReg dest);
  void emitGetGContext(Vout& as, Vreg dest);
  
  void emitTransCounterInc(Asm& a);
  void emitTransCounterInc(Vout&);
  
  void emitIncRef(Asm& as, PhysReg base);
  void emitIncRef(Vout& v, Vreg base);
  void emitIncRefCheckNonStatic(Asm& as, PhysReg base, DataType dtype);
  void emitIncRefGenericRegSafe(Asm& as, PhysReg base, int disp, PhysReg tmpReg);
  
  void emitAssertFlagsNonNegative(Vout& v, Vreg sf);
  void emitAssertRefCount(Vout& v, Vreg base);
  
  void emitMovRegReg(Asm& as, PhysReg srcReg, PhysReg dstReg);
  void emitLea(Asm& as, MemoryRef mr, PhysReg dst);
  
  Vreg emitLdObjClass(Vout& v, Vreg objReg, Vreg dstReg);
  Vreg emitLdClsCctx(Vout& v, Vreg srcReg, Vreg dstReg);
  
  void emitCall(Asm& as, TCA dest, RegSet args);
  void emitCall(Asm& as, CppCall call, RegSet args);
  void emitCall(Vout& v, CppCall call, RegSet args);
  
  // store imm to the 8-byte memory location at ref. Warning: don't use this
  // if you wanted an atomic store; large imms cause two stores.
  void emitImmStoreq(Vout& v, Immed64 imm, Vptr ref);
  void emitImmStoreq(Asm& as, Immed64 imm, MemoryRef ref);
  
  void emitRB(Vout& v, Trace::RingBufferType t, const char* msgm);
  
  void emitTraceCall(CodeBlock& cb, Offset pcOff);
  
  /*
   * Tests the surprise flags for the current thread. Should be used
   * before a jnz to surprise handling code.
   */
  void emitTestSurpriseFlags(Asm& as, PhysReg rds);
  Vreg emitTestSurpriseFlags(Vout& v, Vreg rds);
  
  void emitCheckSurpriseFlagsEnter(Vout& main, Vout& cold, Vreg rds,
                                   Fixup fixup);
  void emitCheckSurpriseFlagsEnter(CodeBlock& mainCode, CodeBlock& coldCode,
                                   PhysReg rds, Fixup fixup);
  
  #ifdef USE_GCC_FAST_TLS
  
  /*
   * TLS access: XXX we currently only support static-style TLS directly
   * linked off of FS.
   *
   * x86 terminology review: "Virtual addresses" are subject to both
   * segmented translation and paged translation. "Linear addresses" are
   * post-segmentation address, subject only to paging. C and C++ generally
   * only have access to bitwise linear addresses.
   *
   * TLS data live at negative virtual addresses off FS: the first datum
   * is typically at VA(FS:-sizeof(datum)). Linux's x64 ABI stores the linear
   * address of the base of TLS at VA(FS:0). While this is just a convention, it
   * is firm: gcc builds binaries that assume it when, e.g., evaluating
   * "&myTlsDatum".
   *
   * The virtual addresses of TLS data are not exposed to C/C++. To figure it
   * out, we take a datum's linear address, and subtract it from the linear
   * address where TLS starts.
   */
  template<typename T>
  inline Vptr getTLSPtr(const T& data) {
    uintptr_t virtualAddress = uintptr_t(&data) - tlsBase();
    return Vptr{baseless(virtualAddress), Vptr::FS};
  }
  
  template<typename T>
  inline void
  emitTLSLoad(Vout& v, const ThreadLocalNoCheck<T>& datum, Vreg reg) {
    v << load{getTLSPtr(datum.m_node.m_p), reg};
  }
  
  template<typename T>
  inline void
  emitTLSLoad(X64Assembler& a, const ThreadLocalNoCheck<T>& datum, Reg64 reg) {
    uintptr_t virtualAddress = uintptr_t(&datum.m_node.m_p) - tlsBase();
    a.fs().loadq(baseless(virtualAddress), reg);
  }
  
  #else // USE_GCC_FAST_TLS
  
  template<typename T>
  inline void
  emitTLSLoad(Vout& v, const ThreadLocalNoCheck<T>& datum, Vreg dest) {
    PhysRegSaver(v, kGPCallerSaved); // we don't know for sure what's alive
    v << ldimmq{datum.m_key, argNumToRegName[0]};
    const CodeAddress addr = (CodeAddress)pthread_getspecific;
    if (deltaFits((uintptr_t)addr, sz::dword)) {
      v << call{addr, argSet(1)};
    } else {
      v << ldimmq{addr, reg::rax};
      v << callr{reg::rax, argSet(1)};
    }
    if (dest != Vreg(reg::rax)) {
      v << copy{reg::rax, dest};
    }
  }
  
  template<typename T>
  inline void
  emitTLSLoad(X64Assembler& a, const ThreadLocalNoCheck<T>& datum, Reg64 dest) {
    PhysRegSaver(a, kGPCallerSaved); // we don't know for sure what's alive
    a.    emitImmReg(datum.m_key, argNumToRegName[0]);
    const TCA addr = (TCA)pthread_getspecific;
    if (deltaFits((uintptr_t)addr, sz::dword)) {
      a.    call(addr);
    } else {
      a.    movq(addr, reg::rax);
      a.    call(reg::rax);
    }
    if (dest != reg::rax) {
      a.    movq(reg::rax, dest);
    }
  }
  
  #endif // USE_GCC_FAST_TLS
  
  // Emit a load of a low pointer.
  void emitLdLowPtr(Vout& v, Vptr mem, Vreg reg, size_t size);
  
  void emitCmpClass(Vout& v, Vreg sf, const Class* c, Vptr mem);
  void emitCmpClass(Vout& v, Vreg sf, Vreg reg, Vptr mem);
  void emitCmpClass(Vout& v, Vreg sf, Vreg reg1, Vreg reg2);
  
  void copyTV(Vout& v, Vloc src, Vloc dst, Type destType);
  void pack2(Vout& v, Vreg s0, Vreg s1, Vreg d0);
  
  Vreg zeroExtendIfBool(Vout& v, const SSATmp* src, Vreg reg);
  
  template<ConditionCode Jcc, class Lambda>
  void jccBlock(Asm& a, Lambda body) {
    Label exit;
    exit.jcc8(a, Jcc);
    body();
    asm_label(a, exit);
  }
  
  /*
   * lookupDestructor --
   *
   * Return a MemoryRef pointer to the destructor for the type in typeReg.
   */
  
  inline MemoryRef lookupDestructor(X64Assembler& a, PhysReg typeReg) {
    auto const table = reinterpret_cast<intptr_t>(g_destructors);
    always_assert_flog(deltaFits(table, sz::dword),
      "Destructor function table is expected to be in the data "
      "segment, with addresses less than 2^31"
    );
    static_assert((KindOfString        >> kShiftDataTypeToDestrIndex == 1) &&
                  (KindOfArray         >> kShiftDataTypeToDestrIndex == 2) &&
                  (KindOfObject        >> kShiftDataTypeToDestrIndex == 3) &&
                  (KindOfResource      >> kShiftDataTypeToDestrIndex == 4) &&
                  (KindOfRef           >> kShiftDataTypeToDestrIndex == 5),
                  "lookup of destructors depends on KindOf* values");
    a.    shrl   (kShiftDataTypeToDestrIndex, r32(typeReg));
    return baseless(typeReg*8 + table);
  }
  
  inline Vptr lookupDestructor(Vout& v, Vreg typeReg) {
    auto const table = reinterpret_cast<intptr_t>(g_destructors);
    always_assert_flog(deltaFits(table, sz::dword),
      "Destructor function table is expected to be in the data "
      "segment, with addresses less than 2^31"
    );
    static_assert((KindOfString        >> kShiftDataTypeToDestrIndex == 1) &&
                  (KindOfArray         >> kShiftDataTypeToDestrIndex == 2) &&
                  (KindOfObject        >> kShiftDataTypeToDestrIndex == 3) &&
                  (KindOfResource      >> kShiftDataTypeToDestrIndex == 4) &&
                  (KindOfRef           >> kShiftDataTypeToDestrIndex == 5),
                  "lookup of destructors depends on KindOf* values");
    auto shiftedType = v.makeReg();
    v << shrli{kShiftDataTypeToDestrIndex, typeReg, shiftedType, v.makeReg()};
    return Vptr{Vreg{}, shiftedType, 8, safe_cast<int>(table)};
  }
  
  //////////////////////////////////////////////////////////////////////
  
  }}}
  
  #endif
  