Arquivos
hhvm/hphp/runtime/vm/jit/extra-data.h
T
Guilherme Ottoni 244d4da93a Replace _ with - in file names under runtime/vm/jit/
Makes things more consistent, at least within this directory.
2013-07-11 15:11:11 -07:00

448 linhas
14 KiB
C++

/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010-2013 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_EXTRADATA_H_
#define incl_HPHP_VM_EXTRADATA_H_
#include "hphp/runtime/vm/jit/ir.h"
namespace HPHP { namespace JIT {
//////////////////////////////////////////////////////////////////////
/*
* Some IRInstructions with compile-time-only constants may carry
* along extra data in the form of one of these structures.
*
* Note that this isn't really appropriate for compile-time constants
* that are actually representing user values (we want them to be
* visible to optimization passes, allocatable to registers, etc),
* just compile-time metadata.
*
* These types must:
*
* - Derive from IRExtraData (for overloading purposes)
* - Be arena-allocatable (no non-trivial destructors)
* - Either CopyConstructible, or implement a clone member
* function that takes an arena to clone to
*
* In addition, for extra data used with a cse-able instruction:
*
* - Implement an cseEquals() member that indicates equality for CSE
* purposes.
* - Implement a cseHash() method.
*
* Finally, optionally they may implement a show() method for use in
* debug printouts.
*/
/*
* Traits that returns the type of the extra C++ data structure for a
* given instruction, if it has one, along with some other information
* about the type.
*/
template<Opcode op> struct OpHasExtraData { enum { value = 0 }; };
template<Opcode op> struct IRExtraDataType;
//////////////////////////////////////////////////////////////////////
struct IRExtraData {};
struct LdSSwitchData : IRExtraData {
struct Elm {
const StringData* str;
Offset dest;
};
explicit LdSSwitchData() = default;
LdSSwitchData(const LdSSwitchData&) = delete;
LdSSwitchData& operator=(const LdSSwitchData&) = delete;
LdSSwitchData* clone(Arena& arena) const {
LdSSwitchData* target = new (arena) LdSSwitchData;
target->func = func;
target->numCases = numCases;
target->defaultOff = defaultOff;
target->cases = new (arena) Elm[numCases];
std::copy(cases, cases + numCases, const_cast<Elm*>(target->cases));
return target;
}
const Func* func;
int64_t numCases;
const Elm* cases;
Offset defaultOff;
};
struct JmpSwitchData : IRExtraData {
JmpSwitchData* clone(Arena& arena) const {
JmpSwitchData* sd = new (arena) JmpSwitchData;
sd->func = func;
sd->base = base;
sd->bounded = bounded;
sd->cases = cases;
sd->defaultOff = defaultOff;
sd->targets = new (arena) Offset[cases];
std::copy(targets, targets + cases, const_cast<Offset*>(sd->targets));
return sd;
}
const Func* func;
int64_t base; // base of switch case
bool bounded; // whether switch is bounded or not
int32_t cases; // number of cases
Offset defaultOff; // offset of default case
Offset* targets; // offsets for all targets
};
struct LocalId : IRExtraData {
explicit LocalId(uint32_t id)
: locId(id)
{}
bool cseEquals(LocalId o) const { return locId == o.locId; }
size_t cseHash() const { return std::hash<uint32_t>()(locId); }
std::string show() const { return folly::to<std::string>(locId); }
uint32_t locId;
};
struct IterId : IRExtraData {
explicit IterId(uint32_t id)
: iterId(id)
{}
bool cseEquals(IterId o) const { return iterId == o.iterId; }
size_t cseHash() const { return std::hash<uint32_t>()(iterId); }
std::string show() const { return folly::to<std::string>(iterId); }
uint32_t iterId;
};
struct FPushCufData : IRExtraData {
FPushCufData(uint32_t a, int32_t id)
: args(a), iterId(id)
{}
bool cseEquals(FPushCufData o) const {
return iterId == o.iterId && args == o.args;
}
size_t cseHash() const {
return std::hash<uint32_t>()(iterId) ^ std::hash<uint32_t>()(args);
}
std::string show() const {
return folly::to<std::string>(iterId, ',', args);
}
uint32_t args;
uint32_t iterId;
};
struct ConstData : IRExtraData {
template<class T>
explicit ConstData(T data)
: m_dataBits(0)
{
static_assert(sizeof(T) <= sizeof m_dataBits,
"Constant data was larger than supported");
static_assert(std::is_pod<T>::value,
"Constant data wasn't a pod?");
const auto toCopy = promoteIfNeeded(data);
std::memcpy(&m_dataBits, &toCopy, sizeof(toCopy));
}
template<typename T>
struct needsPromotion {
static constexpr bool value = std::is_integral<T>::value ||
std::is_same<T,bool>::value ||
std::is_enum<T>::value;
};
template<class T>
typename std::enable_if<needsPromotion<T>::value, int64_t>::type
promoteIfNeeded(T t) { return t; }
template<class T>
typename std::enable_if<!needsPromotion<T>::value, T>::type
promoteIfNeeded(T t) { return t; }
template<class T>
T as() const {
T ret;
std::memcpy(&ret, &m_dataBits, sizeof ret);
return ret;
}
bool cseEquals(ConstData o) const { return m_dataBits == o.m_dataBits; }
size_t cseHash() const { return std::hash<uintptr_t>()(m_dataBits); }
private:
uintptr_t m_dataBits;
};
/*
* Information for the REQ_BIND_JMPCC stubs we create when a tracelet
* ends with conditional jumps.
*/
struct ReqBindJccData : IRExtraData {
Offset taken;
Offset notTaken;
std::string show() const {
return folly::to<std::string>(taken, ',', notTaken);
}
};
/*
* Information for a conditional side exit based on a type check of a
* local or stack cell.
*/
struct SideExitGuardData : IRExtraData {
uint32_t checkedSlot;
Offset taken;
std::string show() const {
return folly::to<std::string>(checkedSlot, ',', taken);
}
};
/*
* Compile-time metadata about an ActRec allocation.
*/
struct ActRecInfo : IRExtraData {
const StringData* invName; // may be nullptr
int32_t numArgs;
std::string show() const {
auto numArgsAndCtorFlag = ActRec::decodeNumArgs(numArgs);
return folly::to<std::string>(numArgsAndCtorFlag.first,
numArgsAndCtorFlag.second ? ",ctor" : "",
invName ? " M" : "");
}
};
/*
* Stack offsets.
*/
struct StackOffset : IRExtraData {
explicit StackOffset(int32_t offset) : offset(offset) {}
std::string show() const { return folly::to<std::string>(offset); }
bool cseEquals(StackOffset o) const { return offset == o.offset; }
size_t cseHash() const { return std::hash<int32_t>()(offset); }
int32_t offset;
};
/*
* Bytecode offsets.
*/
struct BCOffset : IRExtraData {
explicit BCOffset(Offset offset) : offset(offset) {}
std::string show() const { return folly::to<std::string>(offset); }
Offset offset;
};
/*
* DefInlineFP is present when we need to create a frame for inlining.
* This instruction also carries some metadata used by tracebuilder to
* track state during an inlined call.
*/
struct DefInlineFPData : IRExtraData {
std::string show() const {
return folly::to<std::string>(
target->fullName()->data(), "(),", retSPOff, ',', retBCOff
);
}
const Func* target;
Offset retBCOff;
Offset retSPOff;
};
/*
* FCallArray offsets
*/
struct CallArrayData : IRExtraData {
explicit CallArrayData(Offset pcOffset, Offset aft)
: pc(pcOffset), after(aft) {}
std::string show() const { return folly::to<std::string>(pc, ",", after); }
Offset pc, after;
};
/*
* Name of a class constant.
*/
struct ClsCnsName : IRExtraData {
explicit ClsCnsName(const StringData* cls, const StringData* cns)
: clsName(cls)
, cnsName(cns)
{}
std::string show() const {
return folly::to<std::string>(clsName->data(), "::", cnsName->data());
}
const StringData* clsName;
const StringData* cnsName;
};
/*
* The name of a class, and the expected Class* at runtime.
*/
struct CheckDefinedClsData : IRExtraData {
CheckDefinedClsData(const StringData* clsName, const Class* cls)
: clsName(clsName)
, cls(cls)
{}
std::string show() const {
return folly::to<std::string>(clsName->data());
}
const StringData* clsName;
const Class* cls;
};
/*
* Offset and stack deltas for InterpOne.
*/
struct InterpOneData : IRExtraData {
InterpOneData(Offset o, int64_t pop, int64_t push, Op op)
: bcOff(o), cellsPopped(pop), cellsPushed(push), opcode(op) {}
// Offset of the instruction to interpret, in the Unit indicated by
// the current Marker.
Offset bcOff;
// The number of stack cells consumed and produced by the
// instruction, respectively. Includes ActRecs.
int64_t cellsPopped;
int64_t cellsPushed;
// Opcode, in case we need to fix the stack differently. Some byte-
// code instructions modify things below the top of the stack.
Op opcode;
};
//////////////////////////////////////////////////////////////////////
#define X(op, data) \
template<> struct IRExtraDataType<op> { typedef data type; }; \
template<> struct OpHasExtraData<op> { enum { value = 1 }; }; \
static_assert(boost::has_trivial_destructor<data>::value, \
"IR extra data type must be trivially destructible")
X(JmpSwitchDest, JmpSwitchData);
X(LdSSwitchDestFast, LdSSwitchData);
X(LdSSwitchDestSlow, LdSSwitchData);
X(RaiseUninitLoc, LocalId);
X(GuardLoc, LocalId);
X(CheckLoc, LocalId);
X(AssertLoc, LocalId);
X(OverrideLoc, LocalId);
X(LdLocAddr, LocalId);
X(DecRefLoc, LocalId);
X(LdLoc, LocalId);
X(StLoc, LocalId);
X(StLocNT, LocalId);
X(IterFree, IterId);
X(MIterFree, IterId);
X(CIterFree, IterId);
X(DecodeCufIter, IterId);
X(CufIterSpillFrame, FPushCufData);
X(DefConst, ConstData);
X(LdConst, ConstData);
X(SpillFrame, ActRecInfo);
X(GuardStk, StackOffset);
X(CheckStk, StackOffset);
X(CastStk, StackOffset);
X(CoerceStk, StackOffset);
X(AssertStk, StackOffset);
X(AssertStkVal, StackOffset);
X(ReDefSP, StackOffset);
X(ReDefGeneratorSP, StackOffset);
X(DefSP, StackOffset);
X(LdStack, StackOffset);
X(LdStackAddr, StackOffset);
X(DecRefStack, StackOffset);
X(DefInlineFP, DefInlineFPData);
X(ReqBindJmp, BCOffset);
X(ReqBindJmpNoIR, BCOffset);
X(ReqRetranslateNoIR, BCOffset);
X(CallArray, CallArrayData);
X(LdClsCns, ClsCnsName);
X(LookupClsCns, ClsCnsName);
X(ReqBindJmpGt, ReqBindJccData);
X(ReqBindJmpGte, ReqBindJccData);
X(ReqBindJmpLt, ReqBindJccData);
X(ReqBindJmpLte, ReqBindJccData);
X(ReqBindJmpEq, ReqBindJccData);
X(ReqBindJmpNeq, ReqBindJccData);
X(ReqBindJmpSame, ReqBindJccData);
X(ReqBindJmpNSame, ReqBindJccData);
X(ReqBindJmpInstanceOfBitmask, ReqBindJccData);
X(ReqBindJmpNInstanceOfBitmask, ReqBindJccData);
X(ReqBindJmpZero, ReqBindJccData);
X(ReqBindJmpNZero, ReqBindJccData);
X(SideExitGuardLoc, SideExitGuardData);
X(SideExitGuardStk, SideExitGuardData);
X(CheckDefinedClsEq, CheckDefinedClsData);
X(InterpOne, InterpOneData);
#undef X
//////////////////////////////////////////////////////////////////////
template<bool hasExtra, Opcode opc, class T> struct AssertExtraTypes {
static void doassert() {
assert(!"called extra on an opcode without extra data");
}
};
template<Opcode opc, class T> struct AssertExtraTypes<true,opc,T> {
static void doassert() {
typedef typename IRExtraDataType<opc>::type ExtraType;
if (!std::is_same<ExtraType,T>::value) {
assert(!"extra<T> was called with an extra data "
"type that doesn't match the opcode type");
}
}
};
// Asserts that Opcode opc has extradata and it is of type T.
template<class T> void assert_opcode_extra(Opcode opc) {
#define O(opcode, dstinfo, srcinfo, flags) \
case opcode: \
AssertExtraTypes< \
OpHasExtraData<opcode>::value,opcode,T \
>::doassert(); \
break;
switch (opc) { IR_OPCODES default: not_reached(); }
#undef O
}
std::string showExtra(Opcode opc, const IRExtraData* data);
//////////////////////////////////////////////////////////////////////
}}
#endif