2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2015 Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
16 #include "hphp/runtime/vm/jit/irgen-interpone.h"
20 #include "hphp/runtime/vm/jit/minstr-effects.h"
21 #include "hphp/runtime/vm/jit/normalized-instruction.h"
23 #include "hphp/runtime/vm/jit/irgen-exit.h"
24 #include "hphp/runtime/vm/jit/irgen-guards.h"
25 #include "hphp/runtime/vm/jit/irgen-internal.h"
27 namespace HPHP
{ namespace jit
{ namespace irgen
{
31 //////////////////////////////////////////////////////////////////////
33 Type
arithOpResult(Type t1
, Type t2
) {
34 if (!t1
.isKnownDataType() || !t2
.isKnownDataType()) {
39 if (both
.maybe(TDbl
)) return TDbl
;
40 if (both
.maybe(TArr
)) return TArr
;
41 if (both
.maybe(TStr
)) return TCell
;
45 Type
arithOpOverResult(Type t1
, Type t2
) {
46 if (t1
<= TInt
&& t2
<= TInt
) {
49 return arithOpResult(t1
, t2
);
52 Type
bitOpResult(Type t1
, Type t2
) {
53 if (!t1
.isKnownDataType() || !t2
.isKnownDataType()) {
58 if (both
<= TStr
) return TStr
;
62 Type
setOpResult(Type locType
, Type valType
, SetOpOp op
) {
64 case SetOpOp::PlusEqual
:
65 case SetOpOp::MinusEqual
:
66 case SetOpOp::MulEqual
: return arithOpResult(locType
.unbox(), valType
);
67 case SetOpOp::PlusEqualO
:
68 case SetOpOp::MinusEqualO
:
69 case SetOpOp::MulEqualO
: return arithOpOverResult(locType
.unbox(), valType
);
70 case SetOpOp::ConcatEqual
: return TStr
;
71 case SetOpOp::PowEqual
:
72 case SetOpOp::DivEqual
:
73 case SetOpOp::ModEqual
: return TUncountedInit
;
74 case SetOpOp::AndEqual
:
75 case SetOpOp::OrEqual
:
76 case SetOpOp::XorEqual
: return bitOpResult(locType
.unbox(), valType
);
77 case SetOpOp::SlEqual
:
78 case SetOpOp::SrEqual
: return TInt
;
83 uint32_t localInputId(const NormalizedInstruction
& inst
) {
87 return inst
.imm
[1].u_LA
;
90 return inst
.imm
[0].u_LA
;
94 folly::Optional
<Type
> interpOutputType(IRGS
& env
,
95 const NormalizedInstruction
& inst
,
96 folly::Optional
<Type
>& checkTypeType
) {
97 using namespace jit::InstrFlags
;
99 auto locId
= localInputId(inst
);
100 static_assert(std::is_unsigned
<decltype(locId
)>::value
,
101 "locId should be unsigned");
102 assertx(locId
< curFunc(env
)->numLocals());
103 return env
.irb
->localType(locId
, DataTypeSpecific
);
106 auto boxed
= [&] (Type t
) -> Type
{
107 if (t
== TGen
) return TBoxedInitCell
;
108 assertx(t
<= TCell
|| t
<= TBoxedCell
);
109 checkTypeType
= t
<= TBoxedCell
? t
: boxType(t
);
110 return TBoxedInitCell
;
113 auto outFlag
= getInstrInfo(inst
.op()).type
;
114 if (outFlag
== OutFInputL
) {
115 outFlag
= inst
.preppedByRef
? OutVInputL
: OutCInputL
;
116 } else if (outFlag
== OutFInputR
) {
117 outFlag
= inst
.preppedByRef
? OutVInput
: OutCInput
;
121 case OutNull
: return TInitNull
;
122 case OutNullUninit
: return TUninit
;
123 case OutString
: return TStr
;
124 case OutStringImm
: return TStaticStr
;
125 case OutDouble
: return TDbl
;
129 case OutBooleanImm
: return TBool
;
130 case OutInt64
: return TInt
;
131 case OutArray
: return TArr
;
132 case OutArrayImm
: return TArr
; // Should be StaticArr: t2124292
134 case OutThisObject
: return TObj
;
135 case OutResource
: return TRes
;
137 case OutFDesc
: return folly::none
;
138 case OutUnknown
: return TGen
;
140 case OutCns
: return TCell
;
141 case OutVUnknown
: return TBoxedInitCell
;
143 case OutSameAsInput
: return topType(env
, BCSPOffset
{0});
144 case OutVInput
: return boxed(topType(env
, BCSPOffset
{0}));
145 case OutVInputL
: return boxed(localType());
147 case OutFInputR
: not_reached();
149 case OutArith
: return arithOpResult(topType(env
, BCSPOffset
{0}),
150 topType(env
, BCSPOffset
{1}));
151 case OutArithO
: return arithOpOverResult(topType(env
, BCSPOffset
{0}),
152 topType(env
, BCSPOffset
{1}));
154 return bitOpResult(topType(env
, BCSPOffset
{0}),
155 inst
.op() == HPHP::OpBitNot
?
156 TBottom
: topType(env
, BCSPOffset
{1}));
157 case OutSetOp
: return setOpResult(localType(),
158 topType(env
, BCSPOffset
{0}),
159 SetOpOp(inst
.imm
[1].u_OA
));
161 auto ty
= localType().unbox();
162 return ty
<= TDbl
? ty
: TCell
;
164 case OutClassRef
: return TCls
;
165 case OutFPushCufSafe
: return folly::none
;
167 case OutNone
: return folly::none
;
170 auto ttype
= topType(env
, BCSPOffset
{0});
171 if (ttype
<= TCell
) return ttype
;
172 // All instructions that are OutCInput or OutCInputL cannot push uninit or
173 // a ref, so only specific inner types need to be checked.
174 if (ttype
.unbox() < TInitCell
) {
175 checkTypeType
= ttype
.unbox();
181 auto ltype
= localType();
182 if (ltype
<= TCell
) return ltype
;
183 if (ltype
.unbox() < TInitCell
) {
184 checkTypeType
= ltype
.unbox();
192 jit::vector
<InterpOneData::LocalType
>
193 interpOutputLocals(IRGS
& env
,
194 const NormalizedInstruction
& inst
,
195 bool& smashesAllLocals
,
196 folly::Optional
<Type
> pushedType
) {
197 using namespace jit::InstrFlags
;
198 auto const& info
= getInstrInfo(inst
.op());
199 // Anything with Local in its output or a member base input can modify a
201 if (!(info
.out
& Local
) && !(info
.in
& MBase
)) return {};
203 jit::vector
<InterpOneData::LocalType
> locals
;
204 auto setLocType
= [&](uint32_t id
, Type t
) {
205 // Relax the type to something guardable. For InterpOne we don't bother to
206 // keep track of specialized types or inner-ref types. (And note that for
207 // psuedomains we may in fact have to guard on the local type after this.)
208 locals
.emplace_back(id
, relaxToGuardable(t
));
210 auto setImmLocType
= [&](uint32_t id
, Type t
) {
211 setLocType(inst
.imm
[id
].u_LA
, t
);
213 auto const func
= curFunc(env
);
215 auto handleBoxiness
= [&] (Type testTy
, Type useTy
) {
216 return testTy
<= TBoxedCell
? TBoxedInitCell
:
217 testTy
.maybe(TBoxedCell
) ? TGen
:
221 auto const mDefine
= static_cast<unsigned char>(MOpFlags::Define
);
230 smashesAllLocals
= true;
234 switch (inst
.immVec
.locationCode()) {
238 // FPassM may or may not affect local types, depending on whether it is
239 // passing to a parameter that is by reference. If we're InterpOne'ing
240 // it, just assume it might be by reference and smash all the locals.
241 smashesAllLocals
= true;
250 assertx(pushedType
.hasValue());
251 auto locType
= env
.irb
->localType(localInputId(inst
), DataTypeSpecific
);
252 assertx(locType
< TGen
|| curFunc(env
)->isPseudoMain());
254 auto stackType
= pushedType
.value();
255 setImmLocType(0, handleBoxiness(locType
, stackType
));
259 case OpStaticLocInit
:
260 setImmLocType(0, TBoxedInitCell
);
264 setImmLocType(0, TCell
);
268 auto locType
= env
.irb
->localType(localInputId(inst
), DataTypeSpecific
);
269 auto stackType
= topType(env
, BCSPOffset
{0});
270 // SetL preserves reffiness of a local.
271 setImmLocType(0, handleBoxiness(locType
, stackType
));
276 assertx(pushedType
.hasValue());
277 assertx(*pushedType
<= TBoxedCell
);
278 setImmLocType(0, pushedType
.value());
284 setImmLocType(0, TUninit
);
287 // New minstrs are handled extremely conservatively for now.
292 if (inst
.imm
[2].u_OA
& mDefine
) smashesAllLocals
= true;
295 if (inst
.imm
[0].u_OA
& mDefine
) smashesAllLocals
= true;
302 smashesAllLocals
= true;
313 switch (inst
.immVec
.locationCode()) {
315 auto const& mii
= getMInstrInfo(inst
.mInstrOp());
316 auto const& base
= inst
.inputs
[mii
.valCount()];
317 assertx(base
.space
== Location::Local
);
319 // MInstrEffects expects to be used in the context of a normally
320 // translated instruction, not an interpOne. The two important
321 // differences are that the base is normally a PtrTo* and we need to
322 // supply an IR opcode representing the operation. SetWithRefElem is
323 // used instead of SetElem because SetElem makes a few assumptions
324 // about side exits that interpOne won't do.
325 auto const baseType
= env
.irb
->localType(
326 base
.offset
, DataTypeSpecific
328 auto const isUnset
= inst
.op() == OpUnsetM
;
329 auto const isProp
= mcodeIsProp(inst
.immVecM
[0]);
331 if (isUnset
&& isProp
) break;
333 // NullSafe (Q) props don't change the types of locals.
334 if (inst
.immVecM
[0] == MQT
) break;
336 auto op
= isProp
? SetProp
: isUnset
? UnsetElem
: SetWithRefElem
;
337 MInstrEffects
effects(op
, baseType
);
338 if (effects
.baseValChanged
) {
339 auto const ty
= effects
.baseType
.deref();
340 assertx((ty
<= TCell
||
342 curFunc(env
)->isPseudoMain());
343 setLocType(base
.offset
, handleBoxiness(ty
, ty
));
350 smashesAllLocals
= true;
360 setImmLocType(3, TCell
);
364 setImmLocType(2, TBoxedInitCell
);
371 setImmLocType(3, TCell
);
377 setImmLocType(2, TGen
);
380 case OpVerifyParamType
: {
381 auto paramId
= inst
.imm
[0].u_LA
;
382 auto const& tc
= func
->params()[paramId
].typeConstraint
;
383 auto locType
= env
.irb
->localType(localInputId(inst
), DataTypeSpecific
);
384 if (tc
.isArray() && !tc
.isSoft() && !func
->mustBeRef(paramId
) &&
385 (locType
<= TObj
|| locType
.maybe(TBoxedCell
))) {
386 setImmLocType(0, handleBoxiness(locType
, TCell
));
392 if (static_cast<SilenceOp
>(inst
.imm
[0].u_OA
) == SilenceOp::Start
) {
393 setImmLocType(inst
.imm
[0].u_LA
, TInt
);
399 false, "Unknown local-modifying op {}", opcodeToName(inst
.op())
406 //////////////////////////////////////////////////////////////////////
410 void interpOne(IRGS
& env
, const NormalizedInstruction
& inst
) {
411 folly::Optional
<Type
> checkTypeType
;
412 auto stackType
= interpOutputType(env
, inst
, checkTypeType
);
413 auto popped
= getStackPopped(inst
.pc());
414 auto pushed
= getStackPushed(inst
.pc());
415 FTRACE(1, "emitting InterpOne for {}, result = {}, popped {}, pushed {}\n",
417 stackType
.hasValue() ? stackType
->toString() : "<none>",
420 InterpOneData idata
{ offsetFromIRSP(env
, BCSPOffset
{0}) };
421 auto locals
= interpOutputLocals(env
, inst
, idata
.smashesAllLocals
,
423 idata
.nChangedLocals
= locals
.size();
424 idata
.changedLocals
= locals
.data();
426 interpOne(env
, stackType
, popped
, pushed
, idata
);
428 auto const out
= getInstrInfo(inst
.op()).out
;
429 auto const checkIdx
= BCSPOffset
{(out
& InstrFlags::StackIns2
) ? 2
430 : (out
& InstrFlags::StackIns1
) ? 1
432 checkTypeStack(env
, checkIdx
, *checkTypeType
, inst
.nextSk().offset(),
433 true /* outerOnly */);
437 void interpOne(IRGS
& env
, int popped
) {
438 InterpOneData idata
{ offsetFromIRSP(env
, BCSPOffset
{0}) };
439 interpOne(env
, folly::none
, popped
, 0, idata
);
442 void interpOne(IRGS
& env
, Type outType
, int popped
) {
443 InterpOneData idata
{ offsetFromIRSP(env
, BCSPOffset
{0}) };
444 interpOne(env
, outType
, popped
, 1, idata
);
447 void interpOne(IRGS
& env
,
448 folly::Optional
<Type
> outType
,
451 InterpOneData
& idata
) {
452 auto const unit
= curUnit(env
);
453 auto const op
= unit
->getOp(bcOff(env
));
455 idata
.bcOff
= bcOff(env
);
456 idata
.cellsPopped
= popped
;
457 idata
.cellsPushed
= pushed
;
462 opcodeChangesPC(idata
.opcode
) ? InterpOneCF
: InterpOne
,
470 //////////////////////////////////////////////////////////////////////
473 * Instructions that unconditionally are implemented with InterpOne are
477 #define INTERP interpOne(env, *env.currentNormalizedInstruction);
479 void emitFPushObjMethod(IRGS
& env
, int32_t, ObjMethodOp
) { INTERP
}
481 void emitLowInvalid(IRGS
& env
) { std::abort(); }
482 void emitCGetL3(IRGS
& env
, int32_t) { INTERP
}
483 void emitAddElemV(IRGS
& env
) { INTERP
}
484 void emitAddNewElemV(IRGS
& env
) { INTERP
}
485 void emitClsCns(IRGS
& env
, const StringData
*) { INTERP
}
486 void emitExit(IRGS
& env
) { INTERP
}
487 void emitFatal(IRGS
& env
, FatalOp
) { INTERP
}
488 void emitUnwind(IRGS
& env
) { INTERP
}
489 void emitThrow(IRGS
& env
) { INTERP
}
490 void emitCGetN(IRGS
& env
) { INTERP
}
491 void emitCGetQuietN(IRGS
& env
) { INTERP
}
492 void emitVGetN(IRGS
& env
) { INTERP
}
493 void emitIssetN(IRGS
& env
) { INTERP
}
494 void emitEmptyN(IRGS
& env
) { INTERP
}
495 void emitSetN(IRGS
& env
) { INTERP
}
496 void emitSetOpN(IRGS
& env
, SetOpOp
) { INTERP
}
497 void emitSetOpG(IRGS
& env
, SetOpOp
) { INTERP
}
498 void emitSetOpS(IRGS
& env
, SetOpOp
) { INTERP
}
499 void emitIncDecN(IRGS
& env
, IncDecOp
) { INTERP
}
500 void emitIncDecG(IRGS
& env
, IncDecOp
) { INTERP
}
501 void emitIncDecS(IRGS
& env
, IncDecOp
) { INTERP
}
502 void emitBindN(IRGS
& env
) { INTERP
}
503 void emitUnsetN(IRGS
& env
) { INTERP
}
504 void emitUnsetG(IRGS
& env
) { INTERP
}
505 void emitFPassN(IRGS
& env
, int32_t) { INTERP
}
506 void emitFCallUnpack(IRGS
& env
, int32_t) { INTERP
}
507 void emitCufSafeArray(IRGS
& env
) { INTERP
}
508 void emitCufSafeReturn(IRGS
& env
) { INTERP
}
509 void emitIncl(IRGS
& env
) { INTERP
}
510 void emitInclOnce(IRGS
& env
) { INTERP
}
511 void emitReq(IRGS
& env
) { INTERP
}
512 void emitReqDoc(IRGS
& env
) { INTERP
}
513 void emitReqOnce(IRGS
& env
) { INTERP
}
514 void emitEval(IRGS
& env
) { INTERP
}
515 void emitDefTypeAlias(IRGS
& env
, int32_t) { INTERP
}
516 void emitDefCns(IRGS
& env
, const StringData
*) { INTERP
}
517 void emitDefCls(IRGS
& env
, int32_t) { INTERP
}
518 void emitDefFunc(IRGS
& env
, int32_t) { INTERP
}
519 void emitCatch(IRGS
& env
) { INTERP
}
520 void emitContGetReturn(IRGS
& env
) { INTERP
}
521 void emitHighInvalid(IRGS
& env
) { std::abort(); }
523 //////////////////////////////////////////////////////////////////////