Move more IR classes into their own headers

Esse commit está contido em:
bsimmers
2013-05-19 17:08:29 -07:00
commit de Sara Golemon
commit 7f335950f9
12 arquivos alterados com 1028 adições e 909 exclusões
+184
Ver Arquivo
@@ -0,0 +1,184 @@
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- 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_BLOCK_H_
#define incl_HPHP_VM_BLOCK_H_
#include "hphp/runtime/vm/translator/hopt/ir.h"
#include "hphp/runtime/vm/translator/hopt/irinstruction.h"
namespace HPHP { namespace JIT {
/*
* A Block refers to a basic block: single-entry, single-exit, list of
* instructions. The instruction list is an intrusive list, so each
* instruction can only be in one block at a time. Likewise, a block
* can only be owned by one trace at a time.
*
* Block owns the InstructionList, but exposes several list methods itself
* so usually you can use Block directly. These methods also update
* IRInstruction::m_block transparently.
*/
struct Block : boost::noncopyable {
typedef InstructionList::iterator iterator;
typedef InstructionList::const_iterator const_iterator;
// Execution frequency hint; codegen will put Unlikely blocks in astubs.
enum Hint { Neither, Likely, Unlikely };
Block(unsigned id, const Func* func, IRInstruction* label)
: m_trace(nullptr)
, m_func(func)
, m_next(nullptr)
, m_id(id)
, m_preds(nullptr)
, m_hint(Neither)
{
push_back(label);
}
IRInstruction* getLabel() const {
assert(front()->op() == DefLabel);
return front();
}
uint32_t getId() const { return m_id; }
Trace* getTrace() const { return m_trace; }
void setTrace(Trace* t) { m_trace = t; }
void setHint(Hint hint) { m_hint = hint; }
Hint getHint() const { return m_hint; }
void addEdge(IRInstruction* jmp);
void removeEdge(IRInstruction* jmp);
bool isMain() const;
// return the last instruction in the block
IRInstruction* back() const {
assert(!m_instrs.empty());
auto it = m_instrs.end();
return const_cast<IRInstruction*>(&*(--it));
}
// return the first instruction in the block.
IRInstruction* front() const {
assert(!m_instrs.empty());
return const_cast<IRInstruction*>(&*m_instrs.begin());
}
// return the fallthrough block. Should be nullptr if the last
// instruction is a Terminal.
Block* getNext() const { return m_next; }
void setNext(Block* b) { m_next = b; }
// return the target block if the last instruction is a branch.
Block* getTaken() const {
return back()->getTaken();
}
// return the postorder number of this block. (updated each time
// sortBlocks() is called.
unsigned postId() const { return m_postid; }
void setPostId(unsigned id) { m_postid = id; }
// insert inst after this block's label, return an iterator to the
// newly inserted instruction.
iterator prepend(IRInstruction* inst) {
assert(front()->op() == DefLabel);
auto it = begin();
return insert(++it, inst);
}
// return iterator to first instruction after the label
iterator skipLabel() { auto it = begin(); return ++it; }
// return iterator to last instruction
iterator backIter() { auto it = end(); return --it; }
// return an iterator to a specific instruction
iterator iteratorTo(IRInstruction* inst) {
assert(inst->getBlock() == this);
return m_instrs.iterator_to(*inst);
}
// visit each src that provides a value to label->dsts[i]. body
// should take an IRInstruction* and an SSATmp*.
template<typename L>
void forEachSrc(unsigned i, L body) {
for (const EdgeData* n = m_preds; n; n = n->next) {
assert(n->jmp->op() == Jmp_ && n->jmp->getTaken() == this);
body(n->jmp, n->jmp->getSrc(i));
}
}
// return the first src providing a value to label->dsts[i] for
// which body(src) returns true, or nullptr if none are found.
template<typename L>
SSATmp* findSrc(unsigned i, L body) {
for (const EdgeData* n = m_preds; n; n = n->next) {
SSATmp* src = n->jmp->getSrc(i);
if (body(src)) return src;
}
return nullptr;
}
// list-compatible interface; these delegate to m_instrs but also update
// inst.m_block
InstructionList& getInstrs() { return m_instrs; }
bool empty() const { return m_instrs.empty(); }
iterator begin() { return m_instrs.begin(); }
iterator end() { return m_instrs.end(); }
const_iterator begin() const { return m_instrs.begin(); }
const_iterator end() const { return m_instrs.end(); }
iterator insert(iterator pos, IRInstruction* inst) {
inst->setBlock(this);
return m_instrs.insert(pos, *inst);
}
void splice(iterator pos, Block* from, iterator begin, iterator end) {
assert(from != this);
for (auto i = begin; i != end; ++i) (*i).setBlock(this);
m_instrs.splice(pos, from->getInstrs(), begin, end);
}
void push_back(IRInstruction* inst) {
inst->setBlock(this);
return m_instrs.push_back(*inst);
}
template <class Predicate> void remove_if(Predicate p) {
m_instrs.remove_if(p);
}
void erase(iterator pos) {
m_instrs.erase(pos);
}
private:
InstructionList m_instrs; // instructions in this block
Trace* m_trace; // owner of this block.
const Func* m_func; // which func are we in
Block* m_next; // fall-through path; null if back()->isTerminal().
const unsigned m_id; // factory-assigned unique id of this block
unsigned m_postid; // postorder number of this block
EdgeData* m_preds; // head of list of predecessor Jmps
Hint m_hint; // execution frequency hint
};
typedef std::list<Block*> BlockList;
typedef std::forward_list<Block*> BlockPtrList;
}}
#endif
+1 -1
Ver Arquivo
@@ -17,7 +17,7 @@
#ifndef incl_HPHP_VM_CFG_H_
#define incl_HPHP_VM_CFG_H_
#include "hphp/runtime/vm/translator/hopt/ir.h"
#include "hphp/runtime/vm/translator/hopt/block.h"
#include "hphp/runtime/vm/translator/hopt/trace.h"
namespace HPHP { namespace JIT {
+2 -2
Ver Arquivo
@@ -23,9 +23,9 @@
#include "hphp/runtime/vm/translator/hopt/ir.h"
#include "hphp/runtime/vm/translator/hopt/cfg.h"
#include "hphp/runtime/vm/translator/hopt/ssatmp.h"
namespace HPHP {
namespace JIT {
namespace HPHP { namespace JIT {
/*
* Hashtable used for common subexpression elimination. The table maps
+355
Ver Arquivo
@@ -0,0 +1,355 @@
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- 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/translator/hopt/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 MarkerData : IRExtraData {
uint32_t bcOff; // the bytecode offset in unit
int32_t stackOff; // stack off from start of trace
const Func* func; // which func are we in
};
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 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?");
std::memcpy(&m_dataBits, &data, sizeof data);
}
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;
};
struct CreateContData : IRExtraData {
const Func* origFunc;
const Func* genFunc;
};
/*
* EdgeData is linked list node that tracks the set of Jmp_'s that pass values
* to a particular block. Each such Jmp_ has one node, and the block points
* to the list head.
*/
struct EdgeData : IRExtraData {
IRInstruction* jmp; // owner of this edge
EdgeData* next; // next edge to same target
};
/*
* 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 {
return folly::to<std::string>(numArgs, 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;
};
//////////////////////////////////////////////////////////////////////
#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(Marker, MarkerData);
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(DefConst, ConstData);
X(LdConst, ConstData);
X(Jmp_, EdgeData);
X(SpillFrame, ActRecInfo);
X(GuardStk, StackOffset);
X(CheckStk, StackOffset);
X(CastStk, StackOffset);
X(AssertStk, 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(InlineCreateCont, CreateContData);
X(CallArray, CallArrayData);
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);
#undef X
//////////////////////////////////////////////////////////////////////
template<bool hasExtra, Opcode opc, class T> struct AssertExtraTypes {
static void doassert() {
assert(!"called getExtra 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(!"getExtra<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
+13
Ver Arquivo
@@ -31,6 +31,7 @@
#include "hphp/runtime/vm/runtime.h"
#include "hphp/runtime/base/stats.h"
#include "hphp/runtime/vm/translator/hopt/cse.h"
#include "hphp/runtime/vm/translator/hopt/irinstruction.h"
#include "hphp/runtime/vm/translator/hopt/irfactory.h"
#include "hphp/runtime/vm/translator/hopt/linearscan.h"
#include "hphp/runtime/vm/translator/hopt/print.h"
@@ -994,6 +995,18 @@ int32_t spillValueCells(IRInstruction* spillStack) {
return numSrcs - 2;
}
bool isConvIntOrPtrToBool(IRInstruction* instr) {
switch (instr->op()) {
case ConvIntToBool:
return true;
case ConvCellToBool:
return instr->getSrc(0)->type().subtypeOfAny(
Type::Func, Type::Cls, Type::FuncCls, Type::VarEnv, Type::TCA);
default:
return false;
}
}
BlockList sortCfg(Trace* trace, const IRFactory& factory) {
assert(trace->isMain());
BlockList blocks;
+12 -894
Ver Arquivo
@@ -59,6 +59,9 @@ using HPHP::Transl::PhysReg;
using HPHP::Transl::ConditionCode;
struct IRInstruction;
struct SSATmp;
struct Block;
struct Trace;
class FailedIRGen : public std::exception {
public:
@@ -599,335 +602,6 @@ enum Opcode : uint16_t {
IR_NUM_OPCODES
};
//////////////////////////////////////////////////////////////////////
/*
* 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 MarkerData : IRExtraData {
uint32_t bcOff; // the bytecode offset in unit
int32_t stackOff; // stack off from start of trace
const Func* func; // which func are we in
};
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 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?");
std::memcpy(&m_dataBits, &data, sizeof data);
}
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;
};
struct CreateContData : IRExtraData {
const Func* origFunc;
const Func* genFunc;
};
/*
* EdgeData is linked list node that tracks the set of Jmp_'s that pass values
* to a particular block. Each such Jmp_ has one node, and the block points
* to the list head.
*/
struct EdgeData : IRExtraData {
IRInstruction* jmp; // owner of this edge
EdgeData* next; // next edge to same target
};
/*
* 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 {
return folly::to<std::string>(numArgs, 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;
};
//////////////////////////////////////////////////////////////////////
#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(Marker, MarkerData);
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(DefConst, ConstData);
X(LdConst, ConstData);
X(Jmp_, EdgeData);
X(SpillFrame, ActRecInfo);
X(GuardStk, StackOffset);
X(CheckStk, StackOffset);
X(CastStk, StackOffset);
X(AssertStk, 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(InlineCreateCont, CreateContData);
X(CallArray, CallArrayData);
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);
#undef X
//////////////////////////////////////////////////////////////////////
template<bool hasExtra, Opcode opc, class T> struct AssertExtraTypes {
static void doassert() {
assert(!"called getExtra 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(!"getExtra<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);
//////////////////////////////////////////////////////////////////////
/*
* A "query op" is any instruction returning Type::Bool that is both
* branch-fusable and negateable.
@@ -1627,290 +1301,6 @@ using folly::Range;
typedef Range<SSATmp**> SrcRange;
typedef Range<SSATmp*> DstRange;
/*
* IRInstructions must be arena-allocatable.
* (Destructors are not called when they come from IRFactory.)
*/
struct IRInstruction {
enum Id { kTransient = 0xffffffff };
/*
* Create an IRInstruction for the opcode `op'.
*
* IRInstruction creation is usually done through IRFactory or
* TraceBuilder rather than directly.
*/
explicit IRInstruction(Opcode op,
uint32_t numSrcs = 0,
SSATmp** srcs = nullptr)
: m_op(op)
, m_typeParam(Type::None)
, m_numSrcs(numSrcs)
, m_numDsts(0)
, m_id(kTransient)
, m_srcs(srcs)
, m_dst(nullptr)
, m_taken(nullptr)
, m_block(nullptr)
, m_extra(nullptr)
{}
IRInstruction(const IRInstruction&) = delete;
IRInstruction& operator=(const IRInstruction&) = delete;
/*
* Construct an IRInstruction as a deep copy of `inst', using
* arena to allocate memory for its srcs/dests.
*/
explicit IRInstruction(Arena& arena, const IRInstruction* inst, Id id);
/*
* Initialize the source list for this IRInstruction. We must not
* have already had our sources initialized before this function is
* called.
*
* Memory for `srcs' is owned outside of this class and must outlive
* it.
*/
void initializeSrcs(uint32_t numSrcs, SSATmp** srcs) {
assert(!m_srcs && !m_numSrcs);
m_numSrcs = numSrcs;
m_srcs = srcs;
}
/*
* Return access to extra-data on this instruction, for the
* specified opcode type.
*
* Pre: op() == opc
*/
template<Opcode opc>
const typename IRExtraDataType<opc>::type* getExtra() const {
assert(opc == op() && "getExtra type error");
assert(m_extra != nullptr);
return static_cast<typename IRExtraDataType<opc>::type*>(m_extra);
}
template<Opcode opc>
typename IRExtraDataType<opc>::type* getExtra() {
assert(opc == op() && "getExtra type error");
return static_cast<typename IRExtraDataType<opc>::type*>(m_extra);
}
/*
* Return access to extra-data of type T. Requires that
* IRExtraDataType<opc>::type is T for this instruction's opcode.
*
* It's normally preferable to use the version of this function that
* takes the opcode instead of this one. This is for writing code
* that is supposed to be able to handle multiple opcode types that
* share the same kind of extra data.
*/
template<class T> const T* getExtra() const {
auto opcode = op();
if (debug) assert_opcode_extra<T>(opcode);
return static_cast<const T*>(m_extra);
}
/*
* Returns whether or not this opcode has an associated extra data
* struct.
*/
bool hasExtra() const;
/*
* Set the extra-data for this IRInstruction to the given pointer.
* Lifetime is must outlast this IRInstruction (and any of its
* clones).
*/
void setExtra(IRExtraData* data) { assert(!m_extra); m_extra = data; }
/*
* Return the raw extradata pointer, for pretty-printing.
*/
const IRExtraData* rawExtra() const { return m_extra; }
/*
* Clear the extra data pointer in a IRInstruction. Used during
* IRFactory::gen to avoid having dangling IRExtraData*'s into stack
* memory.
*/
void clearExtra() { m_extra = nullptr; }
/*
* Replace an instruction in place with a Nop. This sometimes may
* be a result of a simplification pass.
*/
void convertToNop();
/*
* Replace a branch with a Jmp; used when we have proven the branch
* is always taken.
*/
void convertToJmp();
/*
* Replace an instruction in place with a Mov. Used when we have
* proven that the instruction's side effects are not needed.
*
* TODO: replace with become
*/
void convertToMov();
/*
* Turns this instruction into the target instruction, without
* changing stable fields (id, current block, list fields). The
* existing destination SSATmp(s) will continue to think they came
* from this instruction.
*
* The target instruction may be transient---we'll clone anything we
* need to keep, using factory for any needed memory.
*
* Note: if you want to use this to replace a CSE-able instruction
* you're probably going to have a bad time. For now it's a
* precondition that the current instruction can't CSE.
*
* Pre: other->isTransient() || numDsts() == other->numDsts()
* Pre: !canCSE()
*/
void become(IRFactory*, IRInstruction* other);
/*
* Deep-copy an IRInstruction, using factory to allocate memory for
* the IRInstruction itself, and its srcs/dests.
*/
IRInstruction* clone(IRFactory* factory) const;
Opcode op() const { return m_op; }
void setOpcode(Opcode newOpc) { m_op = newOpc; }
Type getTypeParam() const { return m_typeParam; }
void setTypeParam(Type t) { m_typeParam = t; }
uint32_t getNumSrcs() const { return m_numSrcs; }
void setNumSrcs(uint32_t i) {
assert(i <= m_numSrcs);
m_numSrcs = i;
}
SSATmp* getSrc(uint32_t i) const;
void setSrc(uint32_t i, SSATmp* newSrc);
SrcRange getSrcs() const {
return SrcRange(m_srcs, m_numSrcs);
}
unsigned getNumDsts() const { return m_numDsts; }
SSATmp* getDst() const {
assert(!naryDst());
return m_dst;
}
void setDst(SSATmp* newDst) {
assert(hasDst());
m_dst = newDst;
m_numDsts = newDst ? 1 : 0;
}
/*
* Returns the ith dest of this instruction. i == 0 is treated specially: if
* the instruction has no dests, getDst(0) will return nullptr, and if the
* instruction is not naryDest, getDst(0) will return the single dest.
*/
SSATmp* getDst(unsigned i) const;
DstRange getDsts();
Range<const SSATmp*> getDsts() const;
void setDsts(unsigned numDsts, SSATmp* newDsts) {
assert(naryDst());
m_numDsts = numDsts;
m_dst = newDsts;
}
/*
* Instruction id is stable and useful as an array index.
*/
uint32_t getId() const {
assert(m_id != kTransient);
return m_id;
}
/*
* Returns true if the instruction is in a transient state. That
* is, it's allocated on the stack and we haven't yet committed to
* inserting it in any blocks.
*/
bool isTransient() const { return m_id == kTransient; }
Block* getBlock() const { return m_block; }
void setBlock(Block* b) { m_block = b; }
Trace* getTrace() const;
void setTaken(Block* b);
Block* getTaken() const { return m_taken; }
bool isControlFlowInstruction() const { return m_taken != nullptr; }
bool isBlockEnd() const { return m_taken || isTerminal(); }
/*
* Comparison and hashing for the purposes of CSE-equality.
*
* Pre: canCSE()
*/
bool cseEquals(IRInstruction* inst) const;
size_t cseHash() const;
std::string toString() const;
/*
* Helper accessors for the OpcodeFlag bits for this instruction.
*
* Note that these wrappers have additional logic beyond just
* checking the corresponding flags bit---you should generally use
* these when you have an actual IRInstruction instead of just an
* Opcode enum value.
*/
bool canCSE() const;
bool hasDst() const;
bool naryDst() const;
bool hasMemEffects() const;
bool isRematerializable() const;
bool isNative() const;
bool consumesReferences() const;
bool consumesReference(int srcNo) const;
bool producesReference() const;
bool mayModifyRefs() const;
bool mayRaiseError() const;
bool isEssential() const;
bool isTerminal() const;
bool isPassthrough() const;
SSATmp* getPassthroughValue() const;
bool killsSources() const;
bool killsSource(int srcNo) const;
bool modifiesStack() const;
SSATmp* modifiedStkPtr() const;
// hasMainDst provides raw access to the HasDest flag, for instructions with
// ModifiesStack set.
bool hasMainDst() const;
private:
bool mayReenterHelper() const;
private:
Opcode m_op;
Type m_typeParam;
uint16_t m_numSrcs;
uint16_t m_numDsts;
const Id m_id;
SSATmp** m_srcs;
SSATmp* m_dst; // if HasDest or NaryDest
Block* m_taken; // for branches, guards, and jmp
Block* m_block; // block that owns this instruction
IRExtraData* m_extra;
public:
boost::intrusive::list_member_hook<> m_listNode; // for InstructionList
};
typedef boost::intrusive::member_hook<IRInstruction,
boost::intrusive::list_member_hook<>,
&IRInstruction::m_listNode>
IRInstructionHookOption;
typedef boost::intrusive::list<IRInstruction, IRInstructionHookOption>
InstructionList;
/*
* Given an SSATmp of type Cls, try to find the name of the class.
* Returns nullptr if can't find it.
@@ -1930,100 +1320,17 @@ Type outputType(const IRInstruction*, int dstId = 0);
*/
void assertOperandTypes(const IRInstruction*);
class SSATmp {
public:
uint32_t getId() const { return m_id; }
IRInstruction* inst() const { return m_inst; }
void setInstruction(IRInstruction* i) { m_inst = i; }
Type type() const { return m_type; }
void setType(Type t) { m_type = t; }
bool isBoxed() const { return type().isBoxed(); }
bool isString() const { return isA(Type::Str); }
bool isArray() const { return isA(Type::Arr); }
std::string toString() const;
// XXX: false for Null, etc. Would rather it returns whether we
// have a compile-time constant value.
bool isConst() const {
return m_inst->op() == DefConst ||
m_inst->op() == LdConst;
}
/*
* For SSATmps with a compile-time constant value, the following
* functions allow accessing it.
*
* Pre: inst() &&
* (inst()->op() == DefConst ||
* inst()->op() == LdConst)
*/
bool getValBool() const;
int64_t getValInt() const;
int64_t getValRawInt() const;
double getValDbl() const;
const StringData* getValStr() const;
const ArrayData* getValArr() const;
const Func* getValFunc() const;
const Class* getValClass() const;
const NamedEntity* getValNamedEntity() const;
uintptr_t getValBits() const;
Variant getValVariant() const;
TCA getValTCA() const;
/*
* Returns: Type::subtypeOf(type(), tag).
*
* This should be used for most checks on the types of IRInstruction
* sources.
*/
bool isA(Type tag) const {
return type().subtypeOf(tag);
}
/*
* The maximum number of registers this SSATmp may need allocated.
* This is based on the type of the temporary (some types never have
* regs, some have two, etc).
*/
int numNeededRegs() const;
private:
friend class IRFactory;
friend class TraceBuilder;
// May only be created via IRFactory. Note that this class is never
// destructed, so don't add complex members.
SSATmp(uint32_t opndId, IRInstruction* i, int dstId = 0)
: m_inst(i)
, m_type(outputType(i, dstId))
, m_id(opndId)
{}
SSATmp(const SSATmp&);
SSATmp& operator=(const SSATmp&);
IRInstruction* m_inst;
Type m_type; // type when defined
const uint32_t m_id;
};
int vectorBaseIdx(Opcode opc);
int vectorKeyIdx(Opcode opc);
int vectorValIdx(Opcode opc);
inline int vectorBaseIdx(const IRInstruction* inst) {
return vectorBaseIdx(inst->op());
}
inline int vectorKeyIdx(const IRInstruction* inst) {
return vectorKeyIdx(inst->op());
}
inline int vectorValIdx(const IRInstruction* inst) {
return vectorValIdx(inst->op());
}
int vectorBaseIdx(const IRInstruction* inst);
int vectorKeyIdx(const IRInstruction* inst);
int vectorValIdx(const IRInstruction* inst);
struct VectorEffects {
static bool supported(Opcode op);
static bool supported(const IRInstruction* inst) {
return supported(inst->op());
}
static bool supported(const IRInstruction* inst);
/*
* VectorEffects::get is used to allow multiple different consumers to deal
@@ -2041,34 +1348,10 @@ struct VectorEffects {
StoreLocFunc storeLocValue,
SetLocTypeFunc setLocType);
explicit VectorEffects(const IRInstruction* inst) {
int keyIdx = vectorKeyIdx(inst);
int valIdx = vectorValIdx(inst);
init(inst->op(),
inst->getSrc(vectorBaseIdx(inst))->type(),
keyIdx == -1 ? Type::None : inst->getSrc(keyIdx)->type(),
valIdx == -1 ? Type::None : inst->getSrc(valIdx)->type());
}
template<typename Container>
VectorEffects(Opcode opc, const Container& srcs) {
int keyIdx = vectorKeyIdx(opc);
int valIdx = vectorValIdx(opc);
init(opc,
srcs[vectorBaseIdx(opc)]->type(),
keyIdx == -1 ? Type::None : srcs[keyIdx]->type(),
valIdx == -1 ? Type::None : srcs[valIdx]->type());
}
VectorEffects(Opcode op, Type base, Type key, Type val) {
init(op, base, key, val);
}
VectorEffects(Opcode op, SSATmp* base, SSATmp* key, SSATmp* val) {
auto typeOrNone =
[](SSATmp* val){ return val ? val->type() : Type::None; };
init(op, typeOrNone(base), typeOrNone(key), typeOrNone(val));
}
explicit VectorEffects(const IRInstruction* inst);
VectorEffects(Opcode op, Type base, Type key, Type val);
VectorEffects(Opcode op, SSATmp* base, SSATmp* key, SSATmp* val);
VectorEffects(Opcode opc, const std::vector<SSATmp*>& srcs);
Type baseType;
Type valType;
@@ -2082,161 +1365,6 @@ private:
typedef folly::Range<TCA> TcaRange;
/*
* A Block refers to a basic block: single-entry, single-exit, list of
* instructions. The instruction list is an intrusive list, so each
* instruction can only be in one block at a time. Likewise, a block
* can only be owned by one trace at a time.
*
* Block owns the InstructionList, but exposes several list methods itself
* so usually you can use Block directly. These methods also update
* IRInstruction::m_block transparently.
*/
struct Block : boost::noncopyable {
typedef InstructionList::iterator iterator;
typedef InstructionList::const_iterator const_iterator;
// Execution frequency hint; codegen will put Unlikely blocks in astubs.
enum Hint { Neither, Likely, Unlikely };
Block(unsigned id, const Func* func, IRInstruction* label)
: m_trace(nullptr)
, m_func(func)
, m_next(nullptr)
, m_id(id)
, m_preds(nullptr)
, m_hint(Neither)
{
push_back(label);
}
IRInstruction* getLabel() const {
assert(front()->op() == DefLabel);
return front();
}
uint32_t getId() const { return m_id; }
Trace* getTrace() const { return m_trace; }
void setTrace(Trace* t) { m_trace = t; }
void setHint(Hint hint) { m_hint = hint; }
Hint getHint() const { return m_hint; }
void addEdge(IRInstruction* jmp);
void removeEdge(IRInstruction* jmp);
bool isMain() const;
// return the last instruction in the block
IRInstruction* back() const {
assert(!m_instrs.empty());
auto it = m_instrs.end();
return const_cast<IRInstruction*>(&*(--it));
}
// return the first instruction in the block.
IRInstruction* front() const {
assert(!m_instrs.empty());
return const_cast<IRInstruction*>(&*m_instrs.begin());
}
// return the fallthrough block. Should be nullptr if the last
// instruction is a Terminal.
Block* getNext() const { return m_next; }
void setNext(Block* b) { m_next = b; }
// return the target block if the last instruction is a branch.
Block* getTaken() const {
return back()->getTaken();
}
// return the postorder number of this block. (updated each time
// sortBlocks() is called.
unsigned postId() const { return m_postid; }
void setPostId(unsigned id) { m_postid = id; }
// insert inst after this block's label, return an iterator to the
// newly inserted instruction.
iterator prepend(IRInstruction* inst) {
assert(front()->op() == DefLabel);
auto it = begin();
return insert(++it, inst);
}
// return iterator to first instruction after the label
iterator skipLabel() { auto it = begin(); return ++it; }
// return iterator to last instruction
iterator backIter() { auto it = end(); return --it; }
// return an iterator to a specific instruction
iterator iteratorTo(IRInstruction* inst) {
assert(inst->getBlock() == this);
return m_instrs.iterator_to(*inst);
}
// visit each src that provides a value to label->dsts[i]. body
// should take an IRInstruction* and an SSATmp*.
template<typename L>
void forEachSrc(unsigned i, L body) {
for (const EdgeData* n = m_preds; n; n = n->next) {
assert(n->jmp->op() == Jmp_ && n->jmp->getTaken() == this);
body(n->jmp, n->jmp->getSrc(i));
}
}
// return the first src providing a value to label->dsts[i] for
// which body(src) returns true, or nullptr if none are found.
template<typename L>
SSATmp* findSrc(unsigned i, L body) {
for (const EdgeData* n = m_preds; n; n = n->next) {
SSATmp* src = n->jmp->getSrc(i);
if (body(src)) return src;
}
return nullptr;
}
// list-compatible interface; these delegate to m_instrs but also update
// inst.m_block
InstructionList& getInstrs() { return m_instrs; }
bool empty() const { return m_instrs.empty(); }
iterator begin() { return m_instrs.begin(); }
iterator end() { return m_instrs.end(); }
const_iterator begin() const { return m_instrs.begin(); }
const_iterator end() const { return m_instrs.end(); }
iterator insert(iterator pos, IRInstruction* inst) {
inst->setBlock(this);
return m_instrs.insert(pos, *inst);
}
void splice(iterator pos, Block* from, iterator begin, iterator end) {
assert(from != this);
for (auto i = begin; i != end; ++i) (*i).setBlock(this);
m_instrs.splice(pos, from->getInstrs(), begin, end);
}
void push_back(IRInstruction* inst) {
inst->setBlock(this);
return m_instrs.push_back(*inst);
}
template <class Predicate> void remove_if(Predicate p) {
m_instrs.remove_if(p);
}
void erase(iterator pos) {
m_instrs.erase(pos);
}
private:
InstructionList m_instrs; // instructions in this block
Trace* m_trace; // owner of this block.
const Func* m_func; // which func are we in
Block* m_next; // fall-through path; null if back()->isTerminal().
const unsigned m_id; // factory-assigned unique id of this block
unsigned m_postid; // postorder number of this block
EdgeData* m_preds; // head of list of predecessor Jmps
Hint m_hint; // execution frequency hint
};
typedef std::list<Block*> BlockList;
typedef std::forward_list<Block*> BlockPtrList;
/*
* Remove any instruction if live[iid] == false
*/
@@ -2254,17 +1382,7 @@ void optimizeTrace(Trace*, IRFactory* irFactory);
*/
int32_t spillValueCells(IRInstruction* spillStack);
inline bool isConvIntOrPtrToBool(IRInstruction* instr) {
switch (instr->op()) {
case ConvIntToBool:
return true;
case ConvCellToBool:
return instr->getSrc(0)->type().subtypeOfAny(
Type::Func, Type::Cls, Type::FuncCls, Type::VarEnv, Type::TCA);
default:
return false;
}
}
bool isConvIntOrPtrToBool(IRInstruction* instr);
}}
+1 -1
Ver Arquivo
@@ -16,7 +16,7 @@
#include "hphp/runtime/vm/translator/hopt/irfactory.h"
#include "hphp/runtime/vm/translator/hopt/cfg.h"
#include "hphp/runtime/vm/translator/hopt/block.h"
namespace HPHP { namespace JIT {
@@ -0,0 +1,311 @@
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- 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_IRINSTRUCTION_H_
#define incl_HPHP_VM_IRINSTRUCTION_H_
#include "hphp/runtime/vm/translator/hopt/ir.h"
#include "hphp/runtime/vm/translator/hopt/extradata.h"
namespace HPHP { namespace JIT {
/*
* IRInstructions must be arena-allocatable.
* (Destructors are not called when they come from IRFactory.)
*/
struct IRInstruction {
enum Id { kTransient = 0xffffffff };
/*
* Create an IRInstruction for the opcode `op'.
*
* IRInstruction creation is usually done through IRFactory or
* TraceBuilder rather than directly.
*/
explicit IRInstruction(Opcode op,
uint32_t numSrcs = 0,
SSATmp** srcs = nullptr)
: m_op(op)
, m_typeParam(Type::None)
, m_numSrcs(numSrcs)
, m_numDsts(0)
, m_id(kTransient)
, m_srcs(srcs)
, m_dst(nullptr)
, m_taken(nullptr)
, m_block(nullptr)
, m_extra(nullptr)
{}
IRInstruction(const IRInstruction&) = delete;
IRInstruction& operator=(const IRInstruction&) = delete;
/*
* Construct an IRInstruction as a deep copy of `inst', using
* arena to allocate memory for its srcs/dests.
*/
explicit IRInstruction(Arena& arena, const IRInstruction* inst, Id id);
/*
* Initialize the source list for this IRInstruction. We must not
* have already had our sources initialized before this function is
* called.
*
* Memory for `srcs' is owned outside of this class and must outlive
* it.
*/
void initializeSrcs(uint32_t numSrcs, SSATmp** srcs) {
assert(!m_srcs && !m_numSrcs);
m_numSrcs = numSrcs;
m_srcs = srcs;
}
/*
* Return access to extra-data on this instruction, for the
* specified opcode type.
*
* Pre: op() == opc
*/
template<Opcode opc>
const typename IRExtraDataType<opc>::type* getExtra() const {
assert(opc == op() && "getExtra type error");
assert(m_extra != nullptr);
return static_cast<typename IRExtraDataType<opc>::type*>(m_extra);
}
template<Opcode opc>
typename IRExtraDataType<opc>::type* getExtra() {
assert(opc == op() && "getExtra type error");
return static_cast<typename IRExtraDataType<opc>::type*>(m_extra);
}
/*
* Return access to extra-data of type T. Requires that
* IRExtraDataType<opc>::type is T for this instruction's opcode.
*
* It's normally preferable to use the version of this function that
* takes the opcode instead of this one. This is for writing code
* that is supposed to be able to handle multiple opcode types that
* share the same kind of extra data.
*/
template<class T> const T* getExtra() const {
auto opcode = op();
if (debug) assert_opcode_extra<T>(opcode);
return static_cast<const T*>(m_extra);
}
/*
* Returns whether or not this opcode has an associated extra data
* struct.
*/
bool hasExtra() const;
/*
* Set the extra-data for this IRInstruction to the given pointer.
* Lifetime is must outlast this IRInstruction (and any of its
* clones).
*/
void setExtra(IRExtraData* data) { assert(!m_extra); m_extra = data; }
/*
* Return the raw extradata pointer, for pretty-printing.
*/
const IRExtraData* rawExtra() const { return m_extra; }
/*
* Clear the extra data pointer in a IRInstruction. Used during
* IRFactory::gen to avoid having dangling IRExtraData*'s into stack
* memory.
*/
void clearExtra() { m_extra = nullptr; }
/*
* Replace an instruction in place with a Nop. This sometimes may
* be a result of a simplification pass.
*/
void convertToNop();
/*
* Replace a branch with a Jmp; used when we have proven the branch
* is always taken.
*/
void convertToJmp();
/*
* Replace an instruction in place with a Mov. Used when we have
* proven that the instruction's side effects are not needed.
*
* TODO: replace with become
*/
void convertToMov();
/*
* Turns this instruction into the target instruction, without
* changing stable fields (id, current block, list fields). The
* existing destination SSATmp(s) will continue to think they came
* from this instruction.
*
* The target instruction may be transient---we'll clone anything we
* need to keep, using factory for any needed memory.
*
* Note: if you want to use this to replace a CSE-able instruction
* you're probably going to have a bad time. For now it's a
* precondition that the current instruction can't CSE.
*
* Pre: other->isTransient() || numDsts() == other->numDsts()
* Pre: !canCSE()
*/
void become(IRFactory*, IRInstruction* other);
/*
* Deep-copy an IRInstruction, using factory to allocate memory for
* the IRInstruction itself, and its srcs/dests.
*/
IRInstruction* clone(IRFactory* factory) const;
Opcode op() const { return m_op; }
void setOpcode(Opcode newOpc) { m_op = newOpc; }
Type getTypeParam() const { return m_typeParam; }
void setTypeParam(Type t) { m_typeParam = t; }
uint32_t getNumSrcs() const { return m_numSrcs; }
void setNumSrcs(uint32_t i) {
assert(i <= m_numSrcs);
m_numSrcs = i;
}
SSATmp* getSrc(uint32_t i) const;
void setSrc(uint32_t i, SSATmp* newSrc);
SrcRange getSrcs() const {
return SrcRange(m_srcs, m_numSrcs);
}
unsigned getNumDsts() const { return m_numDsts; }
SSATmp* getDst() const {
assert(!naryDst());
return m_dst;
}
void setDst(SSATmp* newDst) {
assert(hasDst());
m_dst = newDst;
m_numDsts = newDst ? 1 : 0;
}
/*
* Returns the ith dest of this instruction. i == 0 is treated specially: if
* the instruction has no dests, getDst(0) will return nullptr, and if the
* instruction is not naryDest, getDst(0) will return the single dest.
*/
SSATmp* getDst(unsigned i) const;
DstRange getDsts();
Range<const SSATmp*> getDsts() const;
void setDsts(unsigned numDsts, SSATmp* newDsts) {
assert(naryDst());
m_numDsts = numDsts;
m_dst = newDsts;
}
/*
* Instruction id is stable and useful as an array index.
*/
uint32_t getId() const {
assert(m_id != kTransient);
return m_id;
}
/*
* Returns true if the instruction is in a transient state. That
* is, it's allocated on the stack and we haven't yet committed to
* inserting it in any blocks.
*/
bool isTransient() const { return m_id == kTransient; }
Block* getBlock() const { return m_block; }
void setBlock(Block* b) { m_block = b; }
Trace* getTrace() const;
void setTaken(Block* b);
Block* getTaken() const { return m_taken; }
bool isControlFlowInstruction() const { return m_taken != nullptr; }
bool isBlockEnd() const { return m_taken || isTerminal(); }
/*
* Comparison and hashing for the purposes of CSE-equality.
*
* Pre: canCSE()
*/
bool cseEquals(IRInstruction* inst) const;
size_t cseHash() const;
std::string toString() const;
/*
* Helper accessors for the OpcodeFlag bits for this instruction.
*
* Note that these wrappers have additional logic beyond just
* checking the corresponding flags bit---you should generally use
* these when you have an actual IRInstruction instead of just an
* Opcode enum value.
*/
bool canCSE() const;
bool hasDst() const;
bool naryDst() const;
bool hasMemEffects() const;
bool isRematerializable() const;
bool isNative() const;
bool consumesReferences() const;
bool consumesReference(int srcNo) const;
bool producesReference() const;
bool mayModifyRefs() const;
bool mayRaiseError() const;
bool isEssential() const;
bool isTerminal() const;
bool isPassthrough() const;
SSATmp* getPassthroughValue() const;
bool killsSources() const;
bool killsSource(int srcNo) const;
bool modifiesStack() const;
SSATmp* modifiedStkPtr() const;
// hasMainDst provides raw access to the HasDest flag, for instructions with
// ModifiesStack set.
bool hasMainDst() const;
private:
bool mayReenterHelper() const;
private:
Opcode m_op;
Type m_typeParam;
uint16_t m_numSrcs;
uint16_t m_numDsts;
const Id m_id;
SSATmp** m_srcs;
SSATmp* m_dst; // if HasDest or NaryDest
Block* m_taken; // for branches, guards, and jmp
Block* m_block; // block that owns this instruction
IRExtraData* m_extra;
public:
boost::intrusive::list_member_hook<> m_listNode; // for InstructionList
};
typedef boost::intrusive::member_hook<IRInstruction,
boost::intrusive::list_member_hook<>,
&IRInstruction::m_listNode>
IRInstructionHookOption;
typedef boost::intrusive::list<IRInstruction, IRInstructionHookOption>
InstructionList;
}}
#endif
+102
Ver Arquivo
@@ -0,0 +1,102 @@
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- 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_SSATMP_H_
#define incl_HPHP_VM_SSATMP_H_
#include "hphp/runtime/vm/translator/hopt/ir.h"
namespace HPHP { namespace JIT {
class SSATmp {
public:
uint32_t getId() const { return m_id; }
IRInstruction* inst() const { return m_inst; }
void setInstruction(IRInstruction* i) { m_inst = i; }
Type type() const { return m_type; }
void setType(Type t) { m_type = t; }
bool isBoxed() const { return type().isBoxed(); }
bool isString() const { return isA(Type::Str); }
bool isArray() const { return isA(Type::Arr); }
std::string toString() const;
// XXX: false for Null, etc. Would rather it returns whether we
// have a compile-time constant value.
bool isConst() const {
return m_inst->op() == DefConst ||
m_inst->op() == LdConst;
}
/*
* For SSATmps with a compile-time constant value, the following
* functions allow accessing it.
*
* Pre: inst() &&
* (inst()->op() == DefConst ||
* inst()->op() == LdConst)
*/
bool getValBool() const;
int64_t getValInt() const;
int64_t getValRawInt() const;
double getValDbl() const;
const StringData* getValStr() const;
const ArrayData* getValArr() const;
const Func* getValFunc() const;
const Class* getValClass() const;
const NamedEntity* getValNamedEntity() const;
uintptr_t getValBits() const;
Variant getValVariant() const;
TCA getValTCA() const;
/*
* Returns: Type::subtypeOf(type(), tag).
*
* This should be used for most checks on the types of IRInstruction
* sources.
*/
bool isA(Type tag) const {
return type().subtypeOf(tag);
}
/*
* The maximum number of registers this SSATmp may need allocated.
* This is based on the type of the temporary (some types never have
* regs, some have two, etc).
*/
int numNeededRegs() const;
private:
friend class IRFactory;
friend class TraceBuilder;
// May only be created via IRFactory. Note that this class is never
// destructed, so don't add complex members.
SSATmp(uint32_t opndId, IRInstruction* i, int dstId = 0)
: m_inst(i)
, m_type(outputType(i, dstId))
, m_id(opndId)
{}
SSATmp(const SSATmp&);
SSATmp& operator=(const SSATmp&);
IRInstruction* m_inst;
Type m_type; // type when defined
const uint32_t m_id;
};
}}
#endif
+1
Ver Arquivo
@@ -17,6 +17,7 @@
#ifndef incl_HPHP_VM_TRACE_H_
#define incl_HPHP_VM_TRACE_H_
#include "hphp/runtime/vm/translator/hopt/block.h"
#include "hphp/runtime/vm/translator/hopt/ir.h"
namespace HPHP { namespace JIT {
+2
Ver Arquivo
@@ -20,6 +20,8 @@
#include "hphp/util/trace.h"
#include "hphp/runtime/vm/translator/hopt/ir.h"
#include "hphp/runtime/vm/translator/hopt/irinstruction.h"
#include "hphp/runtime/vm/translator/hopt/ssatmp.h"
using namespace HPHP::Transl;
@@ -16,8 +16,9 @@
#include "hphp/runtime/base/strings.h"
#include "hphp/runtime/vm/member_operations.h"
#include "hphp/runtime/vm/translator/hopt/ir.h"
#include "hphp/runtime/vm/translator/hopt/hhbctranslator.h"
#include "hphp/runtime/vm/translator/hopt/ir.h"
#include "hphp/runtime/vm/translator/hopt/irinstruction.h"
#include "hphp/runtime/vm/translator/translator-x64.h"
// These files do ugly things with macros so include them last
@@ -38,6 +39,9 @@ static bool wantPropSpecializedWarnings() {
bool VectorEffects::supported(Opcode op) {
return opcodeHasFlags(op, VectorProp | VectorElem);
}
bool VectorEffects::supported(const IRInstruction* inst) {
return supported(inst->op());
}
void VectorEffects::get(const IRInstruction* inst,
StoreLocFunc storeLocalValue,
@@ -86,6 +90,35 @@ Opcode canonicalOp(Opcode op) {
}
}
VectorEffects::VectorEffects(const IRInstruction* inst) {
int keyIdx = vectorKeyIdx(inst);
int valIdx = vectorValIdx(inst);
init(inst->op(),
inst->getSrc(vectorBaseIdx(inst))->type(),
keyIdx == -1 ? Type::None : inst->getSrc(keyIdx)->type(),
valIdx == -1 ? Type::None : inst->getSrc(valIdx)->type());
}
VectorEffects::VectorEffects(Opcode op, Type base, Type key, Type val) {
init(op, base, key, val);
}
VectorEffects::VectorEffects(Opcode op,
SSATmp* base, SSATmp* key, SSATmp* val) {
auto typeOrNone =
[](SSATmp* val){ return val ? val->type() : Type::None; };
init(op, typeOrNone(base), typeOrNone(key), typeOrNone(val));
}
VectorEffects::VectorEffects(Opcode opc, const std::vector<SSATmp*>& srcs) {
int keyIdx = vectorKeyIdx(opc);
int valIdx = vectorValIdx(opc);
init(opc,
srcs[vectorBaseIdx(opc)]->type(),
keyIdx == -1 ? Type::None : srcs[keyIdx]->type(),
valIdx == -1 ? Type::None : srcs[valIdx]->type());
}
void VectorEffects::init(Opcode op, const Type origBase,
const Type key, const Type origVal) {
baseType = origBase;
@@ -193,6 +226,9 @@ int vectorBaseIdx(Opcode opc) {
: opcodeHasFlags(opc, VectorElem) ? 1
: bad_value<int>();
}
int vectorBaseIdx(const IRInstruction* inst) {
return vectorBaseIdx(inst->op());
}
// vectorKeyIdx returns the src index for inst's key operand.
int vectorKeyIdx(Opcode opc) {
@@ -204,6 +240,9 @@ int vectorKeyIdx(Opcode opc) {
: opcodeHasFlags(opc, VectorElem) ? 2
: bad_value<int>();
}
int vectorKeyIdx(const IRInstruction* inst) {
return vectorKeyIdx(inst->op());
}
// vectorValIdx returns the src index for inst's value operand.
int vectorValIdx(Opcode opc) {
@@ -229,6 +268,9 @@ int vectorValIdx(Opcode opc) {
: bad_value<int>();
}
}
int vectorValIdx(const IRInstruction* inst) {
return vectorValIdx(inst->op());
}
HhbcTranslator::VectorTranslator::VectorTranslator(
const NormalizedInstruction& ni,
@@ -245,20 +287,11 @@ HhbcTranslator::VectorTranslator::VectorTranslator(
{
}
/* Copy varargs SSATmp*s into a vector */
template<typename... Srcs>
static void getSrcs(std::vector<SSATmp*>& srcVec, SSATmp* src, Srcs... srcs) {
srcVec.push_back(src);
getSrcs(srcVec, srcs...);
}
static void getSrcs(std::vector<SSATmp*>& srcVec) {}
template<typename... Srcs>
SSATmp* HhbcTranslator::VectorTranslator::genStk(Opcode opc, Srcs... srcs) {
assert(opcodeHasFlags(opc, HasStackVersion));
assert(!opcodeHasFlags(opc, ModifiesStack));
std::vector<SSATmp*> srcVec;
getSrcs(srcVec, srcs...);
std::vector<SSATmp*> srcVec({srcs...});
SSATmp* base = srcVec[vectorBaseIdx(opc)];
/* If the base is a pointer to a stack cell and the operation might change