Support interpOne in translateRegion

This was going to be easy, but then it wasn't. The existing
interpOne relied heavily on the outputs of NormalizedInstruction, and
those are only populated by code that we want to kill soon. It also
didn't properly deal with bytecodes that wrote to more than one local,
or more than one stack cell. So the bulk of this diff is rewriting
interpOne to not rely on the NI outputs, then it was simple to hook it
into the region translator. I also cleaned things up so we don't
attempt to translate instructions that have no hope of succeeding; we
just interp them on the first try now.
Esse commit está contido em:
bsimmers
2013-06-19 18:55:57 -07:00
commit de Sara Golemon
commit f928f1b320
21 arquivos alterados com 957 adições e 572 exclusões
+12 -6
Ver Arquivo
@@ -363,6 +363,11 @@ OverrideLoc<T,localId> S0:FramePtr
T doesn't have to be related to the local's current type, so this
may be used to update tracked state after InterpOne instructions.
SmashLocals S0:FramePtr
Erases the tracked types and values of all locals in the frame
represented by S0.
D:StkPtr = GuardStk<T,offset> S0:StkPtr
Guard that the type of the cell on the stack pointed to by S0 at
@@ -1449,13 +1454,14 @@ D:Int LdSwitchObjIndex S0:Obj S1:Int S2:Int
in the range [S1:S1 + S2), and if so return the value S1 - (Int)S0.
Else, they return the target of the default target, S2 + 1.
D:StkPtr InterpOne<T> S0:FramePtr S1:StkPtr S2:ConstInt S3:ConstInt
D:StkPtr InterpOne<T, bcOff, numPopped, numPushed> S0:FramePtr S1:StkPtr
Call the interpreter implementation function for one opcode. S0 and
S1 are, respectively, the VM frame and stack pointers before this
instruction. S2 is the bytecode offset. S3 is the stack adjustment
performed by this instruction: number of cells popped minus number
of cells pushed. This instruction returns the updated VM stack
Call the interpreter implementation function for one opcode. S0 and S1 are,
respectively, the VM frame and stack pointers before this instruction. T is
the type of the top stack element after the call, or None if it pushes
nothing. bcOff is the bytecode offset. numPopped is the number of stack cells
consumed by the instruction, and numPushed is the number of stack cells
produced by the instruction. This instruction returns the updated VM stack
pointer.
InterpOneCF S0:FramePtr S1:StkPtr S2:ConstInt S3:ConstInt
+1
Ver Arquivo
@@ -403,6 +403,7 @@ public:
F(bool, ProfileBC, false) \
F(bool, ProfileHWEnable, true) \
F(string, ProfileHWEvents, string("")) \
F(bool, JitAlwaysInterpOne, false) \
F(uint32_t, JitMaxTranslations, 12) \
F(uint64_t, JitGlobalTranslationLimit, -1) \
F(bool, JitTrampolines, true) \
+14 -5
Ver Arquivo
@@ -892,11 +892,20 @@ bool instrIsControlFlow(Op opcode) {
}
bool instrIsNonCallControlFlow(Op opcode) {
return
instrIsControlFlow(opcode) &&
!isFCallStar(opcode) &&
opcode != OpContEnter &&
opcode != OpFCallBuiltin;
if (!instrIsControlFlow(opcode) || isFCallStar(opcode)) return false;
switch (opcode) {
case OpContEnter:
case OpFCallBuiltin:
case OpIncl:
case OpInclOnce:
case OpReq:
case OpReqOnce:
case OpReqDoc:
return false;
default:
return true;
}
}
bool instrAllowsFallThru(Op opcode) {
+8 -7
Ver Arquivo
@@ -87,15 +87,16 @@ int64_t spillSlotsToSize(int n) {
return n * sizeof(int64_t);
}
void cgPunt(const char* file, int line, const char* func, uint32_t bcOff) {
void cgPunt(const char* file, int line, const char* func, uint32_t bcOff,
const Func* vmFunc) {
if (dumpIREnabled()) {
HPHP::Trace::trace("--------- CG_PUNT %s %d %s bcOff: %d \n",
file, line, func, bcOff);
}
throw FailedCodeGen(file, line, func, bcOff);
throw FailedCodeGen(file, line, func, bcOff, vmFunc);
}
#define CG_PUNT(instr) cgPunt(__FILE__, __LINE__, #instr, m_curBcOff)
#define CG_PUNT(instr) cgPunt(__FILE__, __LINE__, #instr, m_curBcOff, curFunc())
struct CycleInfo {
int node;
@@ -354,6 +355,7 @@ NOOP_OPCODE(DefFP)
NOOP_OPCODE(DefSP)
NOOP_OPCODE(AssertLoc)
NOOP_OPCODE(OverrideLoc)
NOOP_OPCODE(SmashLocals)
NOOP_OPCODE(AssertStk)
NOOP_OPCODE(AssertStkVal)
NOOP_OPCODE(Nop)
@@ -5116,9 +5118,8 @@ void CodeGenerator::cgConcat(IRInstruction* inst) {
void CodeGenerator::cgInterpOne(IRInstruction* inst) {
SSATmp* fp = inst->src(0);
SSATmp* sp = inst->src(1);
SSATmp* pcOffTmp = inst->src(2);
SSATmp* spAdjustmentTmp = inst->src(3);
int64_t pcOff = pcOffTmp->getValInt();
auto const& extra = *inst->extra<InterpOne>();
int64_t pcOff = extra.bcOff;
auto opc = *(curFunc()->unit()->at(pcOff));
void* interpOneHelper = interpOneEntryPoints[opc];
@@ -5130,7 +5131,7 @@ void CodeGenerator::cgInterpOne(IRInstruction* inst) {
auto newSpReg = m_regs[inst->dst()].reg();
assert(newSpReg == m_regs[sp].reg());
int64_t spAdjustBytes = cellsToBytes(spAdjustmentTmp->getValInt());
int64_t spAdjustBytes = cellsToBytes(extra.cellsPopped - extra.cellsPushed);
if (spAdjustBytes != 0) {
m_as.addq(spAdjustBytes, newSpReg);
}
-11
Ver Arquivo
@@ -28,17 +28,6 @@
namespace HPHP {
namespace JIT {
class FailedCodeGen : public std::exception {
public:
const char* file;
const int line;
const char* func;
const uint32_t bcOff;
FailedCodeGen(const char* _file, int _line, const char* _func,
uint32_t _bcOff) :
file(_file), line(_line), func(_func), bcOff(_bcOff) { }
};
struct ArgGroup;
// DestType describes the return type of native helper calls, particularly
+18
Ver Arquivo
@@ -317,6 +317,23 @@ struct CheckDefinedClsData : IRExtraData {
const Class* cls;
};
/*
* Offset and stack deltas for InterpOne.
*/
struct InterpOneData : IRExtraData {
InterpOneData(Offset o, int64_t pop, int64_t push)
: bcOff(o), cellsPopped(pop), cellsPushed(push) {}
// 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;
};
//////////////////////////////////////////////////////////////////////
#define X(op, data) \
@@ -380,6 +397,7 @@ X(ReqBindJmpNZero, ReqBindJccData);
X(SideExitGuardLoc, SideExitGuardData);
X(SideExitGuardStk, SideExitGuardData);
X(CheckDefinedClsEq, CheckDefinedClsData);
X(InterpOne, InterpOneData);
#undef X
+275 -26
Ver Arquivo
@@ -217,6 +217,17 @@ void HhbcTranslator::replace(uint32_t index, SSATmp* tmp) {
m_evalStack.replace(index, tmp);
}
Type HhbcTranslator::topType(uint32_t idx) const {
if (idx < m_evalStack.size()) {
return m_evalStack.top(idx)->type();
} else {
auto stkVal = getStackValue(m_tb->sp(),
idx - m_evalStack.size() + m_stackDeficit);
if (stkVal.knownType.equals(Type::None)) return Type::Gen;
return stkVal.knownType;
}
}
/*
* When doing gen-time inlining, we set up a series of IR instructions
* that looks like this:
@@ -503,7 +514,7 @@ void HhbcTranslator::emitAddElemC() {
void HhbcTranslator::emitAddNewElemC() {
if (!topC(1)->isA(Type::Arr)) {
return emitInterpOne(Type::Arr, 2, 0);
return emitInterpOne(Type::Arr, 2);
}
auto const val = popC();
@@ -3278,31 +3289,269 @@ void HhbcTranslator::emitXor() {
gen(DecRef, btr);
}
/**
* Emit InterpOne instruction.
* - 'type' is the return type of the value the instruction pushes on
* the stack if any (or Type:None if none)
* - 'numPopped' is the number of cells that this instruction pops
* - 'numExtraPushed' is the number of cells this instruction pushes on
* the stack, in addition to the cell corresponding to 'type'
*/
void HhbcTranslator::emitInterpOne(Type type, int numPopped,
int numExtraPushed) {
// We're calling into the interpreter so we want the stack synced to memory.
SSATmp* sp = spillStack();
// discard the top elements of the stack, which are consumed by this instr
discard(numPopped);
assert(numPopped == m_stackDeficit);
int numPushed = (type == Type::None ? 0 : 1) + numExtraPushed;
gen(
InterpOne,
type,
m_tb->fp(),
sp,
cns(bcOff()),
cns(numPopped - numPushed)
);
m_stackDeficit = 0;
namespace {
Type arithOpResult(Type t1, Type t2) {
if (!t1.isKnownDataType() || !t2.isKnownDataType()) {
return Type::Cell;
}
auto both = t1 | t2;
if (both.maybe(Type::Dbl)) return Type::Dbl;
if (both.maybe(Type::Arr)) return Type::Arr;
if (both.maybe(Type::Str)) return Type::Cell;
return Type::Int;
}
Type bitOpResult(Type t1, Type t2) {
if (!t1.isKnownDataType() || !t2.isKnownDataType()) {
return Type::Cell;
}
auto both = t1 | t2;
if (both.subtypeOf(Type::Str)) return Type::Str;
return Type::Int;
}
Type setOpResult(Type locType, Type valType, SetOpOp op) {
switch (op) {
case SetOpPlusEqual:
case SetOpMinusEqual:
case SetOpMulEqual: return arithOpResult(locType.unbox(), valType);
case SetOpConcatEqual: return Type::Str;
case SetOpDivEqual:
case SetOpModEqual: return Type::Cell;
case SetOpAndEqual:
case SetOpOrEqual:
case SetOpXorEqual: return bitOpResult(locType.unbox(), valType);
case SetOpSlEqual:
case SetOpSrEqual: return Type::Int;
case SetOp_invalid: not_reached();
}
not_reached();
}
uint32_t localOutputId(const NormalizedInstruction& inst) {
switch (inst.op()) {
case OpUnpackCont:
case OpPackCont:
case OpContRetC:
case OpContSend:
case OpContRaise:
return 0;
case OpSetWithRefLM:
case OpFPassL:
return inst.imm[1].u_IVA;
default:
return inst.imm[0].u_IVA;
}
}
}
Type HhbcTranslator::interpOutputType(const NormalizedInstruction& inst) const {
using namespace Transl::InstrFlags;
auto localType = [&]{
auto locId = localOutputId(inst);
assert(locId >= 0 && locId < curFunc()->numLocals());
auto t = m_tb->getLocalType(locId);
return t.equals(Type::None) ? Type::Gen : t;
};
auto cell = [](Type t) {
return t.unbox();
};
auto boxed = [](Type t) {
if (t.equals(Type::Gen)) return t;
assert(t.isBoxed() || t.notBoxed());
return t.isBoxed() ? t : boxType(t);
};
auto outFlag = getInstrInfo(inst.op()).type;
if (outFlag == OutFInputL) {
outFlag = inst.preppedByRef ? OutVInputL : OutCInputL;
} else if (outFlag == OutFInputR) {
outFlag = inst.preppedByRef ? OutVInput : OutCInput;
}
switch (outFlag) {
case OutNull: return Type::InitNull;
case OutNullUninit: return Type::Uninit;
case OutString: return Type::Str;
case OutStringImm: return Type::StaticStr;
case OutDouble: return Type::Dbl;
case OutBoolean:
case OutBooleanImm: return Type::Bool;
case OutInt64: return Type::Int;
case OutArray: return Type::Arr;
case OutArrayImm: return Type::Arr; // Should be StaticArr: t2124292
case OutObject:
case OutThisObject: return Type::Obj;
case OutFDesc: return Type::None;
case OutUnknown: return Type::Gen;
case OutPred: return inst.outPred;
case OutCns: return Type::Cell;
case OutVUnknown: return Type::BoxedCell;
case OutSameAsInput: return topType(0);
case OutCInput: return cell(topType(0));
case OutVInput: return boxed(topType(0));
case OutCInputL: return cell(localType());
case OutVInputL: return boxed(localType());
case OutFInputL:
case OutFInputR: not_reached();
case OutArith: return arithOpResult(topType(0), topType(1));
case OutBitOp:
return bitOpResult(topType(0),
inst.op() == HPHP::OpBitNot ? Type::Bottom
: topType(1));
case OutSetOp: return setOpResult(localType(), topType(0),
SetOpOp(inst.imm[1].u_OA));
case OutIncDec: return localType().unbox().isInt() ? Type::Int
: Type::Cell;
case OutStrlen: return topType(0).isString() ? Type::Int : Type::Cell;
case OutClassRef: return Type::Cls;
case OutSetM: return Type::Cell; // Imprecise but we can translate
// all cases that matter.
case OutNone: return Type::None;
}
not_reached();
}
void HhbcTranslator::interpOutputLocals(const NormalizedInstruction& inst) {
using namespace Transl::InstrFlags;
if (!(getInstrInfo(inst.op()).out & Local)) return;
auto setImmLocType = [&](uint32_t id, Type t = Type::Gen) {
gen(OverrideLoc, t, LocalId(inst.imm[id].u_HA), m_tb->fp());
};
switch (inst.op()) {
case OpSetN:
case OpSetOpN:
case OpIncDecN:
case OpBindN:
case OpUnsetN:
gen(SmashLocals, m_tb->fp());
break;
case OpSetOpL:
case OpIncDecL: {
auto locType = m_tb->getLocalType(inst.imm[0].u_HA);
auto stackType = topType(0);
setImmLocType(0, locType.isBoxed() ? stackType.box() : stackType);
break;
}
case OpStaticLocInit:
setImmLocType(0, Type::BoxedCell);
break;
case OpInitThisLoc:
setImmLocType(0, Type::Gen);
break;
case OpSetL:
case OpBindL:
setImmLocType(0, topType(0));
break;
case OpUnsetL:
setImmLocType(0, Type::Uninit);
break;
case OpSetM:
case OpSetOpM:
case OpBindM:
case OpVGetM:
case OpSetWithRefLM:
case OpSetWithRefRM:
switch (inst.immVec.locationCode()) {
case LL: {
auto const& mii = getMInstrInfo(inst.mInstrOp());
auto const& base = inst.inputs[mii.valCount()]->location;
assert(base.space == Location::Local);
Type baseType = m_tb->getLocalType(base.offset);
assert(baseType.isBoxed() || baseType.notBoxed() ||
baseType.equals(Type::Gen));
const bool baseBoxed = baseType.isBoxed();
baseType = baseType.strip();
// This is a simple, conservative approximation of the real type flow
// logic that happens in VectorTranslator.
if (baseType.maybe(Type::Str | Type::Bool | Type::Null)) {
auto promoted = mcodeMaybePropName(inst.immVecM[0]) ? Type::Obj
: Type::Arr;
if (!baseType.isNull()) {
// Promotion isn't guaranteed to happen so the base might keep
// its original type.
promoted = promoted | baseType;
}
if (baseBoxed) promoted = boxType(promoted);
gen(OverrideLoc, promoted, LocalId(base.offset), m_tb->fp());
}
break;
}
case LNL:
case LNC:
gen(SmashLocals, m_tb->fp());
break;
default:
break;
}
break;
case OpMIterInitK:
case OpMIterNextK:
setImmLocType(3, Type::Cell);
case OpMIterInit:
case OpMIterNext:
setImmLocType(2, Type::BoxedCell);
break;
case OpIterInitK:
case OpWIterInitK:
case OpIterNextK:
case OpWIterNextK:
setImmLocType(3, Type::Cell);
case OpIterInit:
case OpWIterInit:
case OpIterNext:
case OpWIterNext:
setImmLocType(2, Type::Gen);
break;
default:
not_reached();
}
}
void HhbcTranslator::emitInterpOne(const NormalizedInstruction& inst) {
auto stackType = interpOutputType(inst);
auto popped = getStackPopped(inst);
auto pushed = getStackPushed(inst);
FTRACE(1, "emitting InterpOne for {}, result = {}, popped {}, pushed {}\n",
inst.toString(), stackType.toString(), popped, pushed);
emitInterpOne(stackType, popped, pushed);
interpOutputLocals(inst);
}
void HhbcTranslator::emitInterpOne(Type outType, int popped) {
emitInterpOne(outType, popped, outType.equals(Type::None) ? 0 : 1);
}
void HhbcTranslator::emitInterpOne(Type outType, int popped, int pushed) {
auto sp = spillStack();
gen(InterpOne, outType, InterpOneData(bcOff(), popped, pushed),
m_tb->fp(), sp);
assert(m_stackDeficit == 0);
}
void HhbcTranslator::emitInterpOneCF(int numPopped) {
+10 -1
Ver Arquivo
@@ -151,9 +151,14 @@ struct HhbcTranslator {
// Other public functions for irtranslator.
void setThisAvailable();
void emitInterpOne(Type type, int numPopped, int numExtraPushed = 0);
void emitInterpOne(Type t, int popped);
void emitInterpOne(Type t, int popped, int pushed);
void emitInterpOne(const NormalizedInstruction&);
void emitInterpOneCF(int numPopped);
std::string showStack() const;
bool hasExit() const {
return m_hasExit;
}
/*
* An emit* function for each HHBC opcode.
@@ -664,6 +669,9 @@ private:
void emitMarker();
SSATmp* staticTVCns(const TypedValue*);
Type interpOutputType(const NormalizedInstruction&) const;
void interpOutputLocals(const NormalizedInstruction&);
private: // Exit trace creation routines.
IRTrace* getExitTrace(Offset targetBcOff = -1);
IRTrace* getExitTrace(Offset targetBcOff,
@@ -779,6 +787,7 @@ private:
SSATmp* popF() { return pop(Type::Gen); }
SSATmp* topC(uint32_t i = 0) { return top(Type::Cell, i); }
SSATmp* topV(uint32_t i = 0) { return top(Type::BoxedCell, i); }
Type topType(uint32_t i) const;
std::vector<SSATmp*> peekSpillValues() const;
SSATmp* emitSpillStack(IRTrace* t, SSATmp* sp,
const std::vector<SSATmp*>& spillVals);
+25 -2
Ver Arquivo
@@ -80,6 +80,28 @@ class FailedIRGen : public std::runtime_error {
{}
};
class FailedCodeGen : public std::runtime_error {
public:
const char* file;
const int line;
const char* func;
const Offset bcOff;
const Func* vmFunc;
FailedCodeGen(const char* _file, int _line, const char* _func,
uint32_t _bcOff, const Func* _vmFunc)
: std::runtime_error(folly::format("FailedCodeGen @ {}:{} in {}. {}@{}",
_file, _line, _func,
_vmFunc->fullName()->data(), _bcOff)
.str())
, file(_file)
, line(_line)
, func(_func)
, bcOff(_bcOff)
, vmFunc(_vmFunc)
{}
};
#define SPUNT(instr) do { \
throw FailedIRGen(__FILE__, __LINE__, instr); \
} while(0)
@@ -180,6 +202,7 @@ O(GuardRefs, ND, S(Func) \
S(Int), E) \
O(AssertLoc, ND, S(FramePtr), E) \
O(OverrideLoc, ND, S(FramePtr), E) \
O(SmashLocals, ND, S(FramePtr), E) \
O(BeginCatch, ND, NA, E|Mem) \
O(EndCatch, ND, S(StkPtr), E|Mem) \
O(LdUnwinderValue, DParam, NA, PRc) \
@@ -441,8 +464,8 @@ O(AddNewElem, D(Arr), SUnk, N|Mem|CRc|PRc) \
O(Concat, D(Str), S(Gen) S(Gen), N|Mem|CRc|PRc|Refs) \
O(ArrayAdd, D(Arr), S(Arr) S(Arr), N|Mem|CRc|PRc) \
O(AKExists, D(Bool), S(Cell) S(Cell), C|N) \
O(InterpOne, D(StkPtr), S(FramePtr) S(StkPtr) \
C(Int) C(Int), E|N|Mem|Refs|Er) \
O(InterpOne, D(StkPtr), S(FramePtr) S(StkPtr), \
E|N|Mem|Refs|Er) \
O(InterpOneCF, ND, S(FramePtr) S(StkPtr) \
C(Int), T|E|N|Mem|Refs|Er) \
O(Spill, DofS(0), SUnk, Mem) \
+15 -112
Ver Arquivo
@@ -49,6 +49,7 @@ using namespace reg;
using namespace Util;
using namespace Trace;
using std::max;
using JIT::HhbcTranslator;
TRACE_SET_MOD(hhir);
#ifdef DEBUG
@@ -1414,60 +1415,6 @@ Translator::translateCIterFree(const NormalizedInstruction& i) {
HHIR_EMIT(CIterFree, i.imm[0].u_IVA);
}
// PSEUDOINSTR_DISPATCH is a switch() fragment that routes opcodes to their
// shared handlers, as per the PSEUDOINSTRS macro.
#define PSEUDOINSTR_DISPATCH(func) \
case OpBitAnd: \
case OpBitOr: \
case OpBitXor: \
case OpSub: \
case OpMul: \
func(BinaryArithOp, t, i) \
case OpSame: \
case OpNSame: \
func(SameOp, t, i) \
case OpEq: \
case OpNeq: \
func(EqOp, t, i) \
case OpLt: \
case OpLte: \
case OpGt: \
case OpGte: \
func(LtGtOp, t, i) \
case OpEmptyL: \
case OpCastBool: \
func(UnaryBooleanOp, t, i) \
case OpJmpZ: \
case OpJmpNZ: \
func(BranchOp, t, i) \
case OpSetL: \
case OpBindL: \
func(AssignToLocalOp, t, i) \
case OpFPassC: \
case OpFPassCW: \
case OpFPassCE: \
func(FPassCOp, t, i) \
case OpFPushCuf: \
case OpFPushCufF: \
case OpFPushCufSafe: \
func(FPushCufOp, t, i) \
case OpIssetL: \
case OpIsNullL: \
case OpIsStringL: \
case OpIsArrayL: \
case OpIsIntL: \
case OpIsObjectL: \
case OpIsBoolL: \
case OpIsDoubleL: \
case OpIsNullC: \
case OpIsStringC: \
case OpIsArrayC: \
case OpIsIntC: \
case OpIsObjectC: \
case OpIsBoolC: \
case OpIsDoubleC: \
func(CheckTypeOp, t, i)
// All vector instructions are handled by one HhbcTranslator method.
#define MII(instr, ...) \
void Translator::translate##instr##M(const NormalizedInstruction& ni) { \
@@ -1477,20 +1424,6 @@ MINSTRS
MII(FPass)
#undef MII
void
Translator::translateInstrDefault(const NormalizedInstruction& i) {
const char *opNames[] = {
#define O(name, imm, push, pop, flags) \
"Unimplemented" #name,
OPCODES
#undef O
};
auto const op = i.op();
HHIR_UNIMPLEMENTED_OP(opNames[uint8_t(op)]);
assert(false);
}
void
Translator::translateInstrWork(const NormalizedInstruction& i) {
auto const op = i.op();
@@ -1498,15 +1431,15 @@ Translator::translateInstrWork(const NormalizedInstruction& i) {
switch (op) {
#define CASE(iNm) \
case Op ## iNm: \
translate ## iNm(i); \
translate ## iNm(i); \
break;
#define TRANSLATE(a, b, c) translate ## a(c); break;
#define TRANSLATE(name, inst) translate ## name(inst); break;
INSTRS
PSEUDOINSTR_DISPATCH(TRANSLATE)
#undef TRANSLATE
#undef CASE
default:
translateInstrDefault(i);
default:
not_reached();
}
}
@@ -1557,33 +1490,15 @@ static int getNumPopped(const NormalizedInstruction& i) {
+ (pushesActRec(i.op()) ? kNumActRecCells : 0);
}
/**
* Returns the number of Act-Rec cells that instruction i pushes onto the stack.
*/
static int getNumARCellsPushed(const NormalizedInstruction& i) {
return pushesActRec(i.op()) ? kNumActRecCells : 0;
}
void Translator::interpretInstr(const NormalizedInstruction& i) {
JIT::Type outStkType = JIT::Type::fromDynLocation(i.outStack);
int poppedCells = getNumPopped(i);
int arPushedCells = getNumARCellsPushed(i);
FTRACE(5, "HHIR: BC Instr {} Popped = {} ARCellsPushed = {}\n",
i.toString(), poppedCells, arPushedCells);
FTRACE(5, "HHIR: BC Instr {} Popped = {}\n",
i.toString(), poppedCells);
if (i.changesPC) {
m_hhbcTrans->emitInterpOneCF(poppedCells);
} else {
m_hhbcTrans->emitInterpOne(outStkType, poppedCells, arPushedCells);
if (i.outLocal) {
// HHIR tracks local values and types, so we should inform it about
// the new local type. This is done via an overriding type assertion.
assert(i.outLocal->isLocal());
int32_t locId = i.outLocal->location.offset;
JIT::Type newType = JIT::Type::fromRuntimeType(i.outLocal->rtt);
m_hhbcTrans->overrideTypeLocal(locId, newType);
}
m_hhbcTrans->emitInterpOne(i);
}
}
@@ -1611,7 +1526,7 @@ void Translator::translateInstr(const NormalizedInstruction& i) {
1, true);
}
if (i.interp) {
if (instrMustInterp(i) || i.interp) {
interpretInstr(i);
} else {
translateInstrWork(i);
@@ -1665,21 +1580,9 @@ void TranslatorX64::irEmitResolvedDeps(const ChangeMap& resolvedDeps) {
}
}
static bool supportedInterpOne(const NormalizedInstruction* i) {
switch (i->op()) {
// Instructions that do function return are not supported yet
case OpRetC:
case OpRetV:
case OpContRetC:
case OpNativeImpl:
return false;
default:
return true;
}
}
TranslatorX64::TranslateResult
TranslatorX64::irTranslateTracelet(Tracelet& t) {
FTRACE(2, "attempting to translate tracelet:\n{}\n", t.toString());
const SrcKey &sk = t.m_sk;
SrcRec& srcRec = *getSrcRec(sk);
assert(srcRec.inProgressTailJumps().size() == 0);
@@ -1721,7 +1624,8 @@ TranslatorX64::irTranslateTracelet(Tracelet& t) {
}();
// Translate each instruction in the tracelet
for (auto* ni = t.m_instrStream.first; ni; ni = ni->next) {
for (auto* ni = t.m_instrStream.first; ni && !m_hhbcTrans->hasExit();
ni = ni->next) {
try {
SKTRACE(1, ni->source, "HHIR: translateInstr\n");
Nuller<NormalizedInstruction> niNuller(&m_curNI);
@@ -1730,12 +1634,11 @@ TranslatorX64::irTranslateTracelet(Tracelet& t) {
} catch (JIT::FailedIRGen& fcg) {
// If we haven't tried interpreting ni yet, flag it to be interpreted
// and retry
if (!ni->interp && supportedInterpOne(ni)) {
if (!ni->interp) {
ni->interp = true;
return Retry;
} else {
throw fcg;
}
throw fcg;
}
assert(ni->source.offset() >= curFunc()->base());
// We sometimes leave the tail of a truncated tracelet in place to aid
@@ -1754,7 +1657,7 @@ TranslatorX64::irTranslateTracelet(Tracelet& t) {
// problem, flag it to be interpreted, and retranslate the tracelet.
for (auto ni = t.m_instrStream.first; ni; ni = ni->next) {
if (ni->source.offset() == fcg.bcOff) {
if (!ni->interp && supportedInterpOne(ni)) {
if (!ni->interp) {
ni->interp = true;
TRACE(1, "HHIR: RETRY Translation %d: will interpOne BC instr %s "
"after failing to code-gen \n\n",
+7 -1
Ver Arquivo
@@ -129,11 +129,17 @@ StackValueInfo getStackValue(SSATmp* sp, uint32_t index) {
case InterpOne: {
SSATmp* prevSp = inst->src(1);
int64_t spAdjustment = inst->src(3)->getValInt(); // # popped - # pushed
auto const& extra = *inst->extra<InterpOne>();
int64_t spAdjustment = extra.cellsPopped - extra.cellsPushed;
Type resultType = inst->typeParam();
if (index == 0 && !resultType.equals(Type::None)) {
return StackValueInfo { resultType };
}
// If the index we're looking for is a cell pushed by the InterpOne (other
// than top of stack), we know nothing about its type.
if (index < extra.cellsPushed) return StackValueInfo{ nullptr };
return getStackValue(prevSp, index + spAdjustment);
}
+12 -3
Ver Arquivo
@@ -237,7 +237,8 @@ void TraceBuilder::updateTrackedState(IRInstruction* inst) {
case InterpOne: {
m_spValue = inst->dst();
int64_t stackAdjustment = inst->src(3)->getValInt();
auto const& extra = *inst->extra<InterpOne>();
int64_t stackAdjustment = extra.cellsPopped - extra.cellsPushed;
// push the return value if any and adjust for the popped values
m_spOffset -= stackAdjustment;
break;
@@ -289,6 +290,10 @@ void TraceBuilder::updateTrackedState(IRInstruction* inst) {
inst->typeParam());
break;
case SmashLocals:
clearLocals();
break;
case IterInitK:
case WIterInitK:
// kill the locals to which this instruction stores iter's key and value
@@ -462,14 +467,18 @@ void TraceBuilder::useState(Block* block) {
useState(std::move(state));
}
void TraceBuilder::clearTrackedState() {
killCse(); // clears m_cseHash
void TraceBuilder::clearLocals() {
for (uint32_t i = 0; i < m_localValues.size(); i++) {
m_localValues[i] = nullptr;
}
for (uint32_t i = 0; i < m_localTypes.size(); i++) {
m_localTypes[i] = Type::None;
}
}
void TraceBuilder::clearTrackedState() {
killCse(); // clears m_cseHash
clearLocals();
m_callerAvailableValues.clear();
m_spValue = m_fpValue = nullptr;
m_spOffset = 0;
+1
Ver Arquivo
@@ -339,6 +339,7 @@ private:
void trackDefInlineFP(IRInstruction* inst);
void trackInlineReturn(IRInstruction* inst);
void updateTrackedState(IRInstruction* inst);
void clearLocals();
void clearTrackedState();
void dropLocalRefsInnerTypes();
+230
Ver Arquivo
@@ -0,0 +1,230 @@
/*
+----------------------------------------------------------------------+
| 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_TRANSLATOR_INSTRS_H_
#define incl_HPHP_TRANSLATOR_INSTRS_H_
/*
* Macros used for dispatch in the translator.
*/
#define INSTRS \
CASE(PopC) \
CASE(PopV) \
CASE(PopR) \
CASE(UnboxR) \
CASE(Null) \
CASE(NullUninit) \
CASE(True) \
CASE(False) \
CASE(Int) \
CASE(Double) \
CASE(String) \
CASE(Array) \
CASE(NewArray) \
CASE(NewTuple) \
CASE(NewCol) \
CASE(Nop) \
CASE(AddElemC) \
CASE(AddNewElemC) \
CASE(ColAddElemC) \
CASE(ColAddNewElemC) \
CASE(Cns) \
CASE(DefCns) \
CASE(ClsCnsD) \
CASE(Concat) \
CASE(Add) \
CASE(Xor) \
CASE(Not) \
CASE(Mod) \
CASE(BitNot) \
CASE(CastInt) \
CASE(CastString) \
CASE(CastDouble) \
CASE(CastArray) \
CASE(CastObject) \
CASE(Print) \
CASE(Jmp) \
CASE(Switch) \
CASE(SSwitch) \
CASE(RetC) \
CASE(RetV) \
CASE(NativeImpl) \
CASE(AGetC) \
CASE(AGetL) \
CASE(CGetL) \
CASE(CGetL2) \
CASE(CGetS) \
CASE(CGetM) \
CASE(CGetG) \
CASE(VGetL) \
CASE(VGetG) \
CASE(VGetM) \
CASE(IssetM) \
CASE(EmptyM) \
CASE(AKExists) \
CASE(SetS) \
CASE(SetG) \
CASE(SetM) \
CASE(SetWithRefLM) \
CASE(SetWithRefRM) \
CASE(SetOpL) \
CASE(SetOpM) \
CASE(IncDecL) \
CASE(IncDecM) \
CASE(UnsetL) \
CASE(UnsetM) \
CASE(BindM) \
CASE(FPushFuncD) \
CASE(FPushFunc) \
CASE(FPushClsMethodD) \
CASE(FPushClsMethodF) \
CASE(FPushObjMethodD) \
CASE(FPushCtor) \
CASE(FPushCtorD) \
CASE(FPassR) \
CASE(FPassL) \
CASE(FPassM) \
CASE(FPassS) \
CASE(FPassG) \
CASE(This) \
CASE(BareThis) \
CASE(CheckThis) \
CASE(InitThisLoc) \
CASE(FCall) \
CASE(FCallArray) \
CASE(FCallBuiltin) \
CASE(VerifyParamType) \
CASE(InstanceOfD) \
CASE(StaticLocInit) \
CASE(IterInit) \
CASE(IterInitK) \
CASE(IterNext) \
CASE(IterNextK) \
CASE(WIterInit) \
CASE(WIterInitK) \
CASE(WIterNext) \
CASE(WIterNextK) \
CASE(ReqDoc) \
CASE(DefCls) \
CASE(DefFunc) \
CASE(Self) \
CASE(Parent) \
CASE(ClassExists) \
CASE(InterfaceExists) \
CASE(TraitExists) \
CASE(Dup) \
CASE(CreateCl) \
CASE(CreateCont) \
CASE(ContEnter) \
CASE(ContExit) \
CASE(UnpackCont) \
CASE(PackCont) \
CASE(ContRetC) \
CASE(ContNext) \
CASE(ContSend) \
CASE(ContRaise) \
CASE(ContValid) \
CASE(ContCurrent) \
CASE(ContStopped) \
CASE(ContHandle) \
CASE(Strlen) \
CASE(IncStat) \
CASE(ArrayIdx) \
CASE(FPushCufIter) \
CASE(CIterFree) \
CASE(LateBoundCls) \
CASE(IssetS) \
CASE(IssetG) \
CASE(UnsetG) \
CASE(EmptyS) \
CASE(EmptyG) \
CASE(VGetS) \
CASE(BindS) \
CASE(BindG) \
CASE(IterFree) \
CASE(FPassV) \
CASE(UnsetN) \
CASE(DecodeCufIter) \
// These are instruction-like functions which cover more than one
// opcode.
#define PSEUDOINSTRS \
CASE(BinaryArithOp) \
CASE(SameOp) \
CASE(EqOp) \
CASE(LtGtOp) \
CASE(UnaryBooleanOp) \
CASE(BranchOp) \
CASE(AssignToLocalOp) \
CASE(FPushCufOp) \
CASE(FPassCOp) \
CASE(CheckTypeOp)
// PSEUDOINSTR_DISPATCH is a switch() fragment that routes opcodes to their
// shared handlers, as per the PSEUDOINSTRS macro.
#define PSEUDOINSTR_DISPATCH(func) \
case OpBitAnd: \
case OpBitOr: \
case OpBitXor: \
case OpSub: \
case OpMul: \
func(BinaryArithOp, i) \
case OpSame: \
case OpNSame: \
func(SameOp, i) \
case OpEq: \
case OpNeq: \
func(EqOp, i) \
case OpLt: \
case OpLte: \
case OpGt: \
case OpGte: \
func(LtGtOp, i) \
case OpEmptyL: \
case OpCastBool: \
func(UnaryBooleanOp, i) \
case OpJmpZ: \
case OpJmpNZ: \
func(BranchOp, i) \
case OpSetL: \
case OpBindL: \
func(AssignToLocalOp, i) \
case OpFPassC: \
case OpFPassCW: \
case OpFPassCE: \
func(FPassCOp, i) \
case OpFPushCuf: \
case OpFPushCufF: \
case OpFPushCufSafe: \
func(FPushCufOp, i) \
case OpIssetL: \
case OpIsNullL: \
case OpIsStringL: \
case OpIsArrayL: \
case OpIsIntL: \
case OpIsObjectL: \
case OpIsBoolL: \
case OpIsDoubleL: \
case OpIsNullC: \
case OpIsStringC: \
case OpIsArrayC: \
case OpIsIntC: \
case OpIsObjectC: \
case OpIsBoolC: \
case OpIsDoubleC: \
func(CheckTypeOp, i)
#endif
+14 -58
Ver Arquivo
@@ -2480,6 +2480,10 @@ bool TranslatorX64::handleServiceRequest(TReqInfo& info,
} break;
case REQ_RESUME: {
if (UNLIKELY(vmpc() == 0)) {
g_vmContext->m_fp = 0;
return false;
}
SrcKey dest(curFunc(), vmpc());
sk = dest;
start = getTranslation(TranslArgs(dest, true));
@@ -2726,6 +2730,11 @@ interpOne##opcode(ActRec* ar, Cell* sp, Offset pcOff) { \
assert(toOp(*vmpc()) == Op::opcode); \
VMExecutionContext* ec = g_vmContext; \
Stats::inc(Stats::Instr_InterpOne ## opcode); \
if (Trace::moduleEnabled(Trace::interpOne, 1)) { \
static const StringData* cat = StringData::GetStaticString("interpOne"); \
static const StringData* name = StringData::GetStaticString(#opcode); \
Stats::incStatGrouped(cat, name, 1); \
} \
INC_TPC(interp_one) \
/* Correct for over-counting in TC-stats. */ \
Stats::inc(Stats::Instr_TC, -1); \
@@ -3017,65 +3026,11 @@ int64_t switchObjHelper(ObjectData* o, int64_t base, int64_t nTargets) {
return switchBoundsCheck(ival, base, nTargets);
}
// PSEUDOINSTR_DISPATCH is a switch() fragment that routes opcodes to their
// shared handlers, as per the PSEUDOINSTRS macro.
#define PSEUDOINSTR_DISPATCH(func) \
case OpBitAnd: \
case OpBitOr: \
case OpBitXor: \
case OpSub: \
case OpMul: \
func(BinaryArithOp, t, i) \
case OpSame: \
case OpNSame: \
func(SameOp, t, i) \
case OpEq: \
case OpNeq: \
func(EqOp, t, i) \
case OpLt: \
case OpLte: \
case OpGt: \
case OpGte: \
func(LtGtOp, t, i) \
case OpEmptyL: \
case OpCastBool: \
func(UnaryBooleanOp, t, i) \
case OpJmpZ: \
case OpJmpNZ: \
func(BranchOp, t, i) \
case OpSetL: \
case OpBindL: \
func(AssignToLocalOp, t, i) \
case OpFPassC: \
case OpFPassCW: \
case OpFPassCE: \
func(FPassCOp, t, i) \
case OpFPushCuf: \
case OpFPushCufF: \
case OpFPushCufSafe: \
func(FPushCufOp, t, i) \
case OpIssetL: \
case OpIsNullL: \
case OpIsStringL: \
case OpIsArrayL: \
case OpIsIntL: \
case OpIsObjectL: \
case OpIsBoolL: \
case OpIsDoubleL: \
case OpIsNullC: \
case OpIsStringC: \
case OpIsArrayC: \
case OpIsIntC: \
case OpIsObjectC: \
case OpIsBoolC: \
case OpIsDoubleC: \
func(CheckTypeOp, t, i)
bool
TranslatorX64::dontGuardAnyInputs(Op op) {
switch (op) {
#define CASE(iNm) case Op ## iNm:
#define NOOP(a, b, c)
#define NOOP(...)
INSTRS
PSEUDOINSTR_DISPATCH(NOOP)
return false;
@@ -3248,6 +3203,7 @@ TranslatorX64::translateWork(const TranslArgs& args) {
auto region = JIT::selectRegion(rContext, &t);
TranslateResult result = Retry;
RegionBlacklist regionInterps;
while (result == Retry) {
traceStart(sk.offset());
@@ -3256,7 +3212,7 @@ TranslatorX64::translateWork(const TranslArgs& args) {
if (region) {
try {
assertCleanState();
result = translateRegion(*region);
result = translateRegion(*region, regionInterps);
FTRACE(2, "translateRegion finished with result {}\n",
translateResultName(result));
} catch (const std::exception& e) {
@@ -3272,9 +3228,9 @@ TranslatorX64::translateWork(const TranslArgs& args) {
if (!region || result == Failure) {
FTRACE(1, "trying irTranslateTracelet\n");
assertCleanState();
static const bool requireRegion = getenv("HHVM_REQUIRE_REGION");
assert(!requireRegion);
result = irTranslateTracelet(*tp);
static const bool requireRegion = getenv("HHVM_REQUIRE_REGION");
assert(IMPLIES(region && requireRegion, result != Success));
}
if (result != Success) {
+188 -164
Ver Arquivo
@@ -57,6 +57,7 @@ namespace Transl {
using namespace HPHP;
using HPHP::JIT::Type;
using HPHP::JIT::HhbcTranslator;
TRACE_SET_MOD(trans)
@@ -343,84 +344,6 @@ Translator::tvToLocation(const TypedValue* tv, const TypedValue* frame) {
return Location(Location::Local, offset);
}
/* Opcode type-table. */
enum OutTypeConstraints {
OutNull,
OutNullUninit,
OutString,
OutStringImm, // String w/ precisely known immediate.
OutDouble,
OutBoolean,
OutBooleanImm,
OutInt64,
OutArray,
OutArrayImm,
OutObject,
OutThisObject, // Object from current environment
OutFDesc, // Blows away the current function desc
OutUnknown, // Not known at tracelet compile-time
OutPred, // Unknown, but give prediction a whirl.
OutCns, // Constant; may be known at compile-time
OutVUnknown, // type is V(unknown)
OutSameAsInput, // type is the same as the first stack inpute
OutCInput, // type is C(input)
OutVInput, // type is V(input)
OutCInputL, // type is C(type) of local input
OutVInputL, // type is V(type) of local input
OutFInputL, // type is V(type) of local input if current param is
// by ref, else type is C(type) of local input
OutFInputR, // Like FInputL, but for R's on the stack.
OutArith, // For Add, Sub, Mul
OutBitOp, // For BitAnd, BitOr, BitXor
OutSetOp, // For SetOpL
OutIncDec, // For IncDecL
OutStrlen, // OpStrLen
OutClassRef, // KindOfClass
OutNone
};
/*
* Input codes indicate what an instruction reads, and some other
* things about their behavior. The order these show up in the inputs
* vector is given in getInputs(), and is relevant in a few cases
* (e.g. instructions taking both stack inputs and MVectors).
*/
enum Operands {
None = 0,
Stack3 = 1 << 0,
Stack2 = 1 << 1,
Stack1 = 1 << 2,
StackIns1 = 1 << 3, // Insert an element under top of stack
StackIns2 = 1 << 4, // Insert an element under top 2 of stack
FuncdRef = 1 << 5, // Input to FPass*
FStack = 1 << 6, // output of FPushFuncD and friends
Local = 1 << 7, // Writes to a local
MVector = 1 << 8, // Member-vector input
Iter = 1 << 9, // Iterator in imm[0]
AllLocals = 1 << 10, // All locals (used by RetC)
DontGuardLocal = 1 << 11, // Dont force a guard on behalf of the local input
DontGuardStack1 = 1 << 12, // Dont force a guard on behalf of stack1 input
DontBreakLocal = 1 << 13, // Dont break a tracelet on behalf of the local
DontBreakStack1 = 1 << 14, // Dont break a tracelet on behalf of stack1 input
IgnoreInnerType = 1 << 15, // Instruction doesnt care about the inner types
DontGuardAny = 1 << 16, // Dont force a guard for any input
This = 1 << 17, // Input to CheckThis
StackN = 1 << 18, // pop N cells from stack; n = imm[0].u_IVA
BStackN = 1 << 19, // consume N cells from stack for builtin call;
// n = imm[0].u_IVA
StackTop2 = Stack1 | Stack2,
StackTop3 = Stack1 | Stack2 | Stack3,
StackCufSafe = StackIns1 | FStack
};
Operands
operator|(const Operands& l, const Operands& r) {
return Operands(int(r) | int(l));
}
static int64_t typeToMask(DataType t) {
return (t == KindOfInvalid) ? 1 : (1 << (1 + getDataTypeIndex(t)));
}
@@ -772,20 +695,16 @@ static RuntimeType setOpOutputType(NormalizedInstruction* ni,
case SetOpSlEqual:
case SetOpSrEqual: return RuntimeType(KindOfInt64);
default:
assert(false);
not_reached();
}
NOT_REACHED();
return RuntimeType(KindOfInvalid);
}
static RuntimeType
getDynLocType(const vector<DynLocation*>& inputs,
const Tracelet& t,
Op opcode,
getDynLocType(const SrcKey startSk,
NormalizedInstruction* ni,
Operands op,
OutTypeConstraints constraint,
DynLocation* outDynLoc) {
InstrFlags::OutTypeConstraints constraint) {
using namespace InstrFlags;
auto const& inputs = ni->inputs;
assert(constraint != OutFInputL);
switch (constraint) {
@@ -803,7 +722,7 @@ getDynLocType(const vector<DynLocation*>& inputs,
CS(OutObject, KindOfObject);
#undef CS
case OutPred: {
auto dt = predictOutputs(t.m_sk, ni);
auto dt = predictOutputs(startSk, ni);
if (dt != KindOfInvalid) ni->outputPredicted = true;
return RuntimeType(dt);
}
@@ -897,54 +816,11 @@ getDynLocType(const vector<DynLocation*>& inputs,
// rhs comes before the M-vector elements.
op == OpSetL || op == OpSetN || op == OpSetG || op == OpSetS ||
op == OpBindL || op == OpBindG || op == OpBindS || op == OpBindN ||
op == OpSetM || op == OpBindM ||
op == OpBindM ||
// Dup takes a single element.
op == OpDup
);
if (op == OpSetM) {
/*
* SetM returns null for "invalid" inputs, or a string if the
* base was a string. VectorTranslator ensures that invalid
* inputs or a string output when we weren't expecting it will
* cause a side exit, so we can keep this fairly simple.
*/
if (ni->immVecM.size() > 1) {
// We don't know the type of the base for the final
// operation so we can't assume anything about the output
// type.
return RuntimeType(KindOfAny);
}
// For single-element vectors, we can determine the output
// type from the base.
Type baseType;
switch (ni->immVec.locationCode()) {
case LGL: case LGC:
case LNL: case LNC:
case LSL: case LSC:
baseType = Type::Gen;
break;
default:
baseType = Type::fromRuntimeType(inputs[1]->rtt);
}
const bool setElem = mcodeMaybeArrayOrMapKey(ni->immVecM[0]);
const Type valType = Type::fromRuntimeType(inputs[0]->rtt);
if (setElem && baseType.maybe(Type::Str)) {
if (baseType.isString()) {
// The base is a string so our output is a string.
return RuntimeType(KindOfString);
} else if (!valType.isString()) {
// The base might be a string and our value isn't known to
// be a string. The output type could be Str or valType.
return RuntimeType(KindOfAny);
}
}
}
const int idx = 0; // all currently supported cases.
if (debug) {
@@ -961,6 +837,51 @@ getDynLocType(const vector<DynLocation*>& inputs,
return inputs[idx]->rtt;
}
case OutSetM: {
/*
* SetM returns null for "invalid" inputs, or a string if the base was a
* string. VectorTranslator ensures that invalid inputs or a string
* output when we weren't expecting it will cause a side exit, so we can
* keep this fairly simple.
*/
if (ni->immVecM.size() > 1) {
// We don't know the type of the base for the final operation so we
// can't assume anything about the output
// type.
return RuntimeType(KindOfAny);
}
// For single-element vectors, we can determine the output type from the
// base.
Type baseType;
switch (ni->immVec.locationCode()) {
case LGL: case LGC:
case LNL: case LNC:
case LSL: case LSC:
baseType = Type::Gen;
break;
default:
baseType = Type::fromRuntimeType(inputs[1]->rtt);
}
const bool setElem = mcodeMaybeArrayOrMapKey(ni->immVecM[0]);
const Type valType = Type::fromRuntimeType(inputs[0]->rtt);
if (setElem && baseType.maybe(Type::Str)) {
if (baseType.isString()) {
// The base is a string so our output is a string.
return RuntimeType(KindOfString);
} else if (!valType.isString()) {
// The base might be a string and our value isn't known to
// be a string. The output type could be Str or valType.
return RuntimeType(KindOfAny);
}
}
return inputs[0]->rtt;
}
case OutCInputL: {
assert(inputs.size() >= 1);
const DynLocation* in = inputs[inputs.size() - 1];
@@ -1002,7 +923,7 @@ getDynLocType(const vector<DynLocation*>& inputs,
case OutBitOp: {
assert(inputs.size() == 2 ||
(inputs.size() == 1 && opcode == OpBitNot));
(inputs.size() == 1 && ni->op() == OpBitNot));
if (inputs.size() == 2) {
return bitOpType(inputs[0], inputs[1]);
} else {
@@ -1024,13 +945,7 @@ getDynLocType(const vector<DynLocation*>& inputs,
* NB: this opcode structure is sparse; it cannot just be indexed by
* opcode.
*/
struct InstrInfo {
Operands in;
Operands out;
OutTypeConstraints type; // How are outputs related to inputs?
int stackDelta; // Impact on stack: # cells *pushed*
};
using namespace InstrFlags;
static const struct {
Op op;
InstrInfo info;
@@ -1200,30 +1115,30 @@ static const struct {
{ OpSetL, {Stack1|Local, Stack1|Local, OutSameAsInput, 0 }},
{ OpSetN, {StackTop2, Stack1|Local, OutSameAsInput, -1 }},
{ OpSetG, {StackTop2, Stack1|Local, OutSameAsInput, -1 }},
{ OpSetG, {StackTop2, Stack1, OutSameAsInput, -1 }},
{ OpSetS, {StackTop3, Stack1, OutSameAsInput, -2 }},
{ OpSetM, {MVector|Stack1, Stack1|Local, OutSameAsInput, 0 }},
{ OpSetM, {MVector|Stack1, Stack1|Local, OutSetM, 0 }},
{ OpSetWithRefLM,{MVector|Local , Local, OutNone, 0 }},
{ OpSetWithRefRM,{MVector|Stack1, Local, OutNone, -1 }},
{ OpSetOpL, {Stack1|Local, Stack1|Local, OutSetOp, 0 }},
{ OpSetOpN, {StackTop2, Stack1|Local, OutUnknown, -1 }},
{ OpSetOpG, {StackTop2, Stack1|Local, OutUnknown, -1 }},
{ OpSetOpG, {StackTop2, Stack1, OutUnknown, -1 }},
{ OpSetOpS, {StackTop3, Stack1, OutUnknown, -2 }},
{ OpSetOpM, {MVector|Stack1, Stack1|Local, OutUnknown, 0 }},
{ OpIncDecL, {Local, Stack1|Local, OutIncDec, 1 }},
{ OpIncDecN, {Stack1, Stack1|Local, OutUnknown, 0 }},
{ OpIncDecG, {Stack1, Stack1|Local, OutUnknown, 0 }},
{ OpIncDecG, {Stack1, Stack1, OutUnknown, 0 }},
{ OpIncDecS, {StackTop2, Stack1, OutUnknown, -1 }},
{ OpIncDecM, {MVector, Stack1, OutUnknown, 1 }},
{ OpBindL, {Stack1|Local|
IgnoreInnerType, Stack1|Local, OutSameAsInput, 0 }},
{ OpBindN, {StackTop2, Stack1|Local, OutSameAsInput, -1 }},
{ OpBindG, {StackTop2, Stack1|Local, OutSameAsInput, -1 }},
{ OpBindG, {StackTop2, Stack1, OutSameAsInput, -1 }},
{ OpBindS, {StackTop3, Stack1, OutSameAsInput, -2 }},
{ OpBindM, {MVector|Stack1, Stack1|Local, OutSameAsInput, 0 }},
{ OpUnsetL, {Local, Local, OutNone, 0 }},
{ OpUnsetN, {Stack1, Local, OutNone, -1 }},
{ OpUnsetG, {Stack1, Local, OutNone, -1 }},
{ OpUnsetG, {Stack1, None, OutNone, -1 }},
{ OpUnsetM, {MVector, None, OutNone, 0 }},
/*** 8. Call instructions ***/
@@ -1262,9 +1177,9 @@ static const struct {
{ OpFPushCufSafe,{StackTop2|DontGuardAny,
StackCufSafe, OutFDesc,
kNumActRecCells }},
{ OpFPassC, {FuncdRef, None, OutNull, 0 }},
{ OpFPassCW, {FuncdRef, None, OutNull, 0 }},
{ OpFPassCE, {FuncdRef, None, OutNull, 0 }},
{ OpFPassC, {FuncdRef, None, OutSameAsInput, 0 }},
{ OpFPassCW, {FuncdRef, None, OutSameAsInput, 0 }},
{ OpFPassCE, {FuncdRef, None, OutSameAsInput, 0 }},
{ OpFPassV, {Stack1|FuncdRef, Stack1, OutUnknown, 0 }},
{ OpFPassR, {Stack1|FuncdRef, Stack1, OutFInputR, 0 }},
{ OpFPassL, {Local|FuncdRef, Stack1, OutFInputL, 1 }},
@@ -1376,11 +1291,81 @@ static void initInstrInfo() {
}
}
const InstrInfo& getInstrInfo(Op op) {
assert(instrInfoInited);
return instrInfo[op];
}
static int numHiddenStackInputs(const NormalizedInstruction& ni) {
assert(ni.immVec.isValid());
return ni.immVec.numStackValues();
}
namespace {
int64_t countOperands(uint64_t mask) {
const uint64_t ignore = FuncdRef | Local | Iter | AllLocals |
DontGuardLocal | DontGuardStack1 | DontBreakLocal | DontBreakStack1 |
IgnoreInnerType | DontGuardAny | This;
mask &= ~ignore;
static const uint64_t counts[][2] = {
{Stack3, 1},
{Stack2, 1},
{Stack1, 1},
{StackIns1, 2},
{StackIns2, 3},
{FStack, kNumActRecCells},
};
int64_t count = 0;
for (auto const& pair : counts) {
if (mask & pair[0]) {
count += pair[1];
mask &= ~pair[0];
}
}
assert(mask == 0);
return count;
}
}
int64_t getStackPopped(const NormalizedInstruction& ni) {
switch (ni.op()) {
case OpFCall: return ni.imm[0].u_IVA + kNumActRecCells;
case OpFCallArray: return kNumActRecCells + 1;
case OpFCallBuiltin:
case OpNewTuple:
case OpCreateCl: return ni.imm[0].u_IVA;
default: break;
}
uint64_t mask = getInstrInfo(ni.op()).in;
int64_t count = 0;
if (mask & MVector) {
count += ni.immVec.numStackValues();
mask &= ~MVector;
}
if (mask & (StackN | BStackN)) {
count += ni.imm[0].u_IVA;
mask &= ~(StackN | BStackN);
}
return count + countOperands(mask);
}
int64_t getStackPushed(const NormalizedInstruction& ni) {
switch (ni.op()) {
case OpFPushCufSafe: return kNumActRecCells + 2;
default: break;
}
return countOperands(getInstrInfo(ni.op()).out);
}
int getStackDelta(const NormalizedInstruction& ni) {
int hiddenStackInputs = 0;
initInstrInfo();
@@ -1404,7 +1389,7 @@ int getStackDelta(const NormalizedInstruction& ni) {
hiddenStackInputs = numHiddenStackInputs(ni);
SKTRACE(2, ni.source, "Has %d hidden stack inputs\n", hiddenStackInputs);
}
int delta = instrInfo[op].stackDelta - hiddenStackInputs;
int delta = instrInfo[op].numPushed - hiddenStackInputs;
return delta;
}
@@ -1957,6 +1942,7 @@ bool outputDependsOnInput(const Op instr) {
case OutBitOp:
case OutSetOp:
case OutIncDec:
case OutSetM:
return true;
}
not_reached();
@@ -1978,7 +1964,7 @@ void Translator::getOutputs(/*inout*/ Tracelet& t,
varEnvTaint = false;
const vector<DynLocation*>& inputs = ni->inputs;
Op op = ni->op();
const Op op = ni->op();
initInstrInfo();
assert_not_implemented(instrInfo.find(op) != instrInfo.end());
@@ -2076,9 +2062,7 @@ void Translator::getOutputs(/*inout*/ Tracelet& t,
op == OpSetM || op == OpSetOpM ||
op == OpBindM ||
op == OpSetWithRefLM || op == OpSetWithRefRM ||
op == OpIncDecL || op == OpIncDecG ||
op == OpUnsetG || op == OpBindG ||
op == OpSetG || op == OpSetOpG ||
op == OpIncDecL ||
op == OpVGetM ||
op == OpStaticLocInit || op == OpInitThisLoc ||
op == OpSetL || op == OpBindL ||
@@ -2100,11 +2084,6 @@ void Translator::getOutputs(/*inout*/ Tracelet& t,
ni->outLocal = incDecLoc;
continue; // Doesn't mutate a loc's types for int. Carry on.
}
if (op == OpSetG || op == OpSetOpG ||
op == OpUnsetG || op == OpBindG ||
op == OpIncDecG) {
continue;
}
if (op == OpUnsetL) {
assert(ni->inputs.size() == 1);
DynLocation* inLoc = ni->inputs[0];
@@ -2298,8 +2277,7 @@ void Translator::getOutputs(/*inout*/ Tracelet& t,
}
DynLocation* dl = t.newDynLocation();
dl->location = loc;
dl->rtt = getDynLocType(inputs, t, op, ni, (Operands)opnd,
typeInfo, dl);
dl->rtt = getDynLocType(t.m_sk, ni, typeInfo);
SKTRACE(2, ni->source, "recording output t(%d->%d) #(%s, %d)\n",
dl->rtt.outerType(), dl->rtt.innerType(),
dl->location.spaceName(), dl->location.offset);
@@ -3767,6 +3745,24 @@ void Translator::readMetaData(Unit::MetaHandle& handle,
} while (handle.nextArg(info));
}
bool Translator::instrMustInterp(const NormalizedInstruction& inst) {
if (RuntimeOption::EvalJitAlwaysInterpOne) return true;
switch (inst.op()) {
// Generate a case for each instruction we support at least partially.
# define CASE(name) case Op##name:
INSTRS
# undef CASE
# define NOTHING(...) // PSEUDOINSTR_DISPATCH has the cases in it
PSEUDOINSTR_DISPATCH(NOTHING)
# undef NOTHING
return false;
default:
return true;
}
}
static Location toLocation(const RegionDesc::Location& loc) {
typedef RegionDesc::Location::Tag T;
switch (loc.tag()) {
@@ -3780,7 +3776,8 @@ static Location toLocation(const RegionDesc::Location& loc) {
}
Translator::TranslateResult
Translator::translateRegion(const RegionDesc& region) {
Translator::translateRegion(const RegionDesc& region,
RegionBlacklist& toInterp) {
typedef JIT::RegionDesc::Block Block;
FTRACE(1, "translateRegion starting with:\n{}\n", show(region));
assert(!region.blocks.empty());
@@ -3823,6 +3820,11 @@ Translator::translateRegion(const RegionDesc& region) {
inst.changesPC = opcodeChangesPC(inst.op());
populateImmediates(inst);
// We can get a more precise output type for interpOne if we know all of
// its inputs, so we still populate the rest of the instruction even if
// this is true.
inst.interp = toInterp.count(sk);
// Apply the first round of metadata from the repo and get a list of
// input locations.
InputInfos inputInfos;
@@ -3884,7 +3886,15 @@ Translator::translateRegion(const RegionDesc& region) {
// Emit IR for the body of the instruction.
Util::Nuller<NormalizedInstruction> niNuller(&m_curNI);
m_curNI = &inst;
translateInstr(inst);
try {
translateInstr(inst);
} catch (const JIT::FailedIRGen& exn) {
FTRACE(1, "ir generation for {} failed with {}\n",
inst.toString(), exn.what());
always_assert(!toInterp.count(sk));
toInterp.insert(sk);
return Retry;
}
// Check the prediction. If the predicted type is less specific than what
// is currently on the eval stack, checkTypeLocation won't emit any code.
@@ -3901,7 +3911,21 @@ Translator::translateRegion(const RegionDesc& region) {
}
traceEnd();
traceCodeGen();
try {
traceCodeGen();
} catch (const JIT::FailedCodeGen& exn) {
FTRACE(1, "code generation failed with {}\n", exn.what());
SrcKey sk{exn.vmFunc, exn.bcOff};
// Until we can trust the placement of Marker instructions, we can't assert
// that this sk isn't already in the interp set. t2424830
if (toInterp.count(sk)) {
return Failure;
}
toInterp.insert(sk);
return Retry;
}
return Success;
}
+101 -157
Ver Arquivo
@@ -33,9 +33,11 @@
#include "hphp/runtime/base/execution_context.h"
#include "hphp/runtime/vm/bytecode.h"
#include "hphp/runtime/vm/jit/fixup.h"
#include "hphp/runtime/vm/jit/region_selection.h"
#include "hphp/runtime/vm/jit/runtime-type.h"
#include "hphp/runtime/vm/jit/srcdb.h"
#include "hphp/runtime/vm/jit/trans-data.h"
#include "hphp/runtime/vm/jit/translator-instrs.h"
#include "hphp/runtime/vm/jit/type.h"
#include "hphp/runtime/vm/jit/writelease.h"
#include "hphp/runtime/vm/debugger_hook.h"
@@ -47,7 +49,6 @@ namespace HPHP {
namespace JIT {
class HhbcTranslator;
class IRFactory;
class RegionDesc;
}
namespace Debug {
class DebugInfo;
@@ -669,159 +670,6 @@ struct TranslArgs {
bool m_interp;
};
#define INSTRS \
CASE(PopC) \
CASE(PopV) \
CASE(PopR) \
CASE(UnboxR) \
CASE(Null) \
CASE(NullUninit) \
CASE(True) \
CASE(False) \
CASE(Int) \
CASE(Double) \
CASE(String) \
CASE(Array) \
CASE(NewArray) \
CASE(NewTuple) \
CASE(NewCol) \
CASE(Nop) \
CASE(AddElemC) \
CASE(AddNewElemC) \
CASE(ColAddElemC) \
CASE(ColAddNewElemC) \
CASE(Cns) \
CASE(DefCns) \
CASE(ClsCnsD) \
CASE(Concat) \
CASE(Add) \
CASE(Xor) \
CASE(Not) \
CASE(Mod) \
CASE(BitNot) \
CASE(CastInt) \
CASE(CastString) \
CASE(CastDouble) \
CASE(CastArray) \
CASE(CastObject) \
CASE(Print) \
CASE(Jmp) \
CASE(Switch) \
CASE(SSwitch) \
CASE(RetC) \
CASE(RetV) \
CASE(NativeImpl) \
CASE(AGetC) \
CASE(AGetL) \
CASE(CGetL) \
CASE(CGetL2) \
CASE(CGetS) \
CASE(CGetM) \
CASE(CGetG) \
CASE(VGetL) \
CASE(VGetG) \
CASE(VGetM) \
CASE(IssetM) \
CASE(EmptyM) \
CASE(AKExists) \
CASE(SetS) \
CASE(SetG) \
CASE(SetM) \
CASE(SetWithRefLM) \
CASE(SetWithRefRM) \
CASE(SetOpL) \
CASE(SetOpM) \
CASE(IncDecL) \
CASE(IncDecM) \
CASE(UnsetL) \
CASE(UnsetM) \
CASE(BindM) \
CASE(FPushFuncD) \
CASE(FPushFunc) \
CASE(FPushClsMethodD) \
CASE(FPushClsMethodF) \
CASE(FPushObjMethodD) \
CASE(FPushCtor) \
CASE(FPushCtorD) \
CASE(FPassR) \
CASE(FPassL) \
CASE(FPassM) \
CASE(FPassS) \
CASE(FPassG) \
CASE(This) \
CASE(BareThis) \
CASE(CheckThis) \
CASE(InitThisLoc) \
CASE(FCall) \
CASE(FCallArray) \
CASE(FCallBuiltin) \
CASE(VerifyParamType) \
CASE(InstanceOfD) \
CASE(StaticLocInit) \
CASE(IterInit) \
CASE(IterInitK) \
CASE(IterNext) \
CASE(IterNextK) \
CASE(WIterInit) \
CASE(WIterInitK) \
CASE(WIterNext) \
CASE(WIterNextK) \
CASE(ReqDoc) \
CASE(DefCls) \
CASE(DefFunc) \
CASE(Self) \
CASE(Parent) \
CASE(ClassExists) \
CASE(InterfaceExists) \
CASE(TraitExists) \
CASE(Dup) \
CASE(CreateCl) \
CASE(CreateCont) \
CASE(ContEnter) \
CASE(ContExit) \
CASE(UnpackCont) \
CASE(PackCont) \
CASE(ContRetC) \
CASE(ContNext) \
CASE(ContSend) \
CASE(ContRaise) \
CASE(ContValid) \
CASE(ContCurrent) \
CASE(ContStopped) \
CASE(ContHandle) \
CASE(Strlen) \
CASE(IncStat) \
CASE(ArrayIdx) \
CASE(FPushCufIter) \
CASE(CIterFree) \
CASE(LateBoundCls) \
CASE(IssetS) \
CASE(IssetG) \
CASE(UnsetG) \
CASE(EmptyS) \
CASE(EmptyG) \
CASE(VGetS) \
CASE(BindS) \
CASE(BindG) \
CASE(IterFree) \
CASE(FPassV) \
CASE(UnsetN) \
CASE(DecodeCufIter) \
// These are instruction-like functions which cover more than one
// opcode.
#define PSEUDOINSTRS \
CASE(BinaryArithOp) \
CASE(SameOp) \
CASE(EqOp) \
CASE(LtGtOp) \
CASE(UnaryBooleanOp) \
CASE(BranchOp) \
CASE(AssignToLocalOp) \
CASE(FPushCufOp) \
CASE(FPassCOp) \
CASE(CheckTypeOp)
/*
* Translator annotates a tracelet with input/output locations/types.
*/
@@ -907,11 +755,11 @@ protected:
virtual void traceCodeGen() = 0;
void traceEnd();
void traceFree();
static bool instrMustInterp(const NormalizedInstruction&);
private:
void interpretInstr(const NormalizedInstruction& i);
void translateInstrWork(const NormalizedInstruction& i);
void translateInstrDefault(const NormalizedInstruction& i);
void passPredictedAndInferredTypes(const NormalizedInstruction& i);
#define CASE(nm) void translate ## nm(const NormalizedInstruction& i);
INSTRS
@@ -930,7 +778,13 @@ protected:
void requestResetHighLevelTranslator();
void populateImmediates(NormalizedInstruction&);
TranslateResult translateRegion(const RegionDesc& region);
/* translateRegion reads from the RegionBlacklist to determine when
* to interpret an instruction, and adds failed instructions to the
* blacklist so they're interpreted on the next attempt. */
typedef hphp_hash_set<SrcKey, SrcKey::Hasher> RegionBlacklist;
TranslateResult translateRegion(const RegionDesc& region,
RegionBlacklist& interp);
TCA m_resumeHelper;
TCA m_resumeHelperRet;
@@ -938,7 +792,7 @@ protected:
typedef std::map<TCA, TransID> TransDB;
TransDB m_transDB;
vector<TransRec> m_translations;
vector<uint64_t*> m_transCounters;
vector<uint64_t*> m_transCounters;
int64_t m_createdTime;
@@ -1115,6 +969,8 @@ public:
};
int getStackDelta(const NormalizedInstruction& ni);
int64_t getStackPopped(const NormalizedInstruction&);
int64_t getStackPushed(const NormalizedInstruction&);
enum class ControlFlowInfo {
None,
@@ -1212,6 +1068,94 @@ static inline bool isSmartPtrRef(DataType t) {
t == KindOfArray || t == KindOfObject;
}
namespace InstrFlags {
enum OutTypeConstraints {
OutNull,
OutNullUninit,
OutString,
OutStringImm, // String w/ precisely known immediate.
OutDouble,
OutBoolean,
OutBooleanImm,
OutInt64,
OutArray,
OutArrayImm,
OutObject,
OutThisObject, // Object from current environment
OutFDesc, // Blows away the current function desc
OutUnknown, // Not known at tracelet compile-time
OutPred, // Unknown, but give prediction a whirl.
OutCns, // Constant; may be known at compile-time
OutVUnknown, // type is V(unknown)
OutSameAsInput, // type is the same as the first stack inpute
OutCInput, // type is C(input)
OutVInput, // type is V(input)
OutCInputL, // type is C(type) of local input
OutVInputL, // type is V(type) of local input
OutFInputL, // type is V(type) of local input if current param is
// by ref, else type is C(type) of local input
OutFInputR, // Like FInputL, but for R's on the stack.
OutArith, // For Add, Sub, Mul
OutBitOp, // For BitAnd, BitOr, BitXor
OutSetOp, // For SetOpL
OutIncDec, // For IncDecL
OutStrlen, // OpStrLen
OutClassRef, // KindOfClass
OutSetM, // SetM is a very special snowflake
OutNone
};
/*
* Input codes indicate what an instruction reads, and some other
* things about their behavior. The order these show up in the inputs
* vector is given in getInputs(), and is relevant in a few cases
* (e.g. instructions taking both stack inputs and MVectors).
*/
enum Operands {
None = 0,
Stack3 = 1 << 0,
Stack2 = 1 << 1,
Stack1 = 1 << 2,
StackIns1 = 1 << 3, // Insert an element under top of stack
StackIns2 = 1 << 4, // Insert an element under top 2 of stack
FuncdRef = 1 << 5, // Input to FPass*
FStack = 1 << 6, // output of FPushFuncD and friends
Local = 1 << 7, // Writes to a local
MVector = 1 << 8, // Member-vector input
Iter = 1 << 9, // Iterator in imm[0]
AllLocals = 1 << 10, // All locals (used by RetC)
DontGuardLocal = 1 << 11, // Dont force a guard on behalf of the local input
DontGuardStack1 = 1 << 12, // Dont force a guard on behalf of stack1 input
DontBreakLocal = 1 << 13, // Dont break a tracelet on behalf of the local
DontBreakStack1 = 1 << 14, // Dont break a tracelet on behalf of stack1 input
IgnoreInnerType = 1 << 15, // Instruction doesnt care about the inner types
DontGuardAny = 1 << 16, // Dont force a guard for any input
This = 1 << 17, // Input to CheckThis
StackN = 1 << 18, // pop N cells from stack; n = imm[0].u_IVA
BStackN = 1 << 19, // consume N cells from stack for builtin call;
// n = imm[0].u_IVA
StackTop2 = Stack1 | Stack2,
StackTop3 = Stack1 | Stack2 | Stack3,
StackCufSafe = StackIns1 | FStack
};
inline Operands operator|(const Operands& l, const Operands& r) {
return Operands(int(r) | int(l));
}
}
struct InstrInfo {
InstrFlags::Operands in;
InstrFlags::Operands out;
InstrFlags::OutTypeConstraints type; // How are outputs related to inputs?
int numPushed;
};
const InstrInfo& getInstrInfo(Op op);
} } // HPHP::Transl
#endif
+17 -18
Ver Arquivo
@@ -157,23 +157,6 @@ Type builtinReturn(const IRInstruction* inst) {
not_reached();
}
Type boxReturn(const IRInstruction* inst, int srcId) {
auto t = inst->src(srcId)->type();
// If t contains Uninit, replace it with InitNull.
t = t.maybe(Type::Uninit) ? (t - Type::Uninit) | Type::InitNull : t;
// We don't try to track when a BoxedStaticStr might be converted to
// a BoxedStr, and we never guard on staticness for strings, so
// boxing a string needs to forget this detail. Same thing for
// arrays.
if (t.subtypeOf(Type::Str)) {
t = Type::Str;
} else if (t.subtypeOf(Type::Arr)) {
t = Type::Arr;
}
// Everything else is just a pure type-system boxing operation.
return t.box();
}
Type stkReturn(const IRInstruction* inst, int dstId,
std::function<Type()> inner) {
assert(inst->modifiesStack());
@@ -202,6 +185,22 @@ Type binArithResultType(Opcode op, Type t1, Type t2) {
}
Type boxType(Type t) {
// If t contains Uninit, replace it with InitNull.
t = t.maybe(Type::Uninit) ? (t - Type::Uninit) | Type::InitNull : t;
// We don't try to track when a BoxedStaticStr might be converted to
// a BoxedStr, and we never guard on staticness for strings, so
// boxing a string needs to forget this detail. Same thing for
// arrays.
if (t.subtypeOf(Type::Str)) {
t = Type::Str;
} else if (t.subtypeOf(Type::Arr)) {
t = Type::Arr;
}
// Everything else is just a pure type-system boxing operation.
return t.box();
}
Type outputType(const IRInstruction* inst, int dstId) {
#define IRT(name, ...) UNUSED static const Type name = Type::name;
IR_TYPES
@@ -210,7 +209,7 @@ Type outputType(const IRInstruction* inst, int dstId) {
#define D(type) return Type::type;
#define DofS(n) return inst->src(n)->type();
#define DUnbox(n) return inst->src(n)->type().unbox();
#define DBox(n) return boxReturn(inst, n);
#define DBox(n) return boxType(inst->src(n)->type());
#define DParam return inst->typeParam();
#define DMulti return Type::None;
#define DStk(in) return stkReturn(inst, dstId, \
+6
Ver Arquivo
@@ -493,6 +493,12 @@ static_assert(sizeof(Type) <= 2 * sizeof(uint64_t),
*/
Type liveTVType(const TypedValue* tv);
/*
* Return the boxed version of the input type, taking into account php
* semantics and subtle implementation details.
*/
Type boxType(Type);
}}
#endif
+2 -1
Ver Arquivo
@@ -86,7 +86,8 @@ class Init {
if (mod >= 0) {
levels[mod] = level;
}
if (mod == Trace::minstr) {
if (mod == Trace::minstr ||
mod == Trace::interpOne) {
levels[Trace::statgroups] = std::max(levels[Trace::statgroups], 1);
}
}
+1
Ver Arquivo
@@ -72,6 +72,7 @@ namespace Trace {
TM(treadmill) \
TM(regalloc) \
TM(bcinterp) \
TM(interpOne) \
TM(refcount) \
TM(asmx64) \
TM(runtime) \