2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present 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 +----------------------------------------------------------------------+
17 #include "hphp/runtime/vm/jit/irgen-interpone.h"
21 #include "hphp/runtime/vm/hhbc.h"
22 #include "hphp/runtime/vm/jit/location.h"
23 #include "hphp/runtime/vm/jit/minstr-effects.h"
25 #include "hphp/runtime/vm/jit/irgen-exit.h"
26 #include "hphp/runtime/vm/jit/irgen-internal.h"
28 namespace HPHP
{ namespace jit
{ namespace irgen
{
32 //////////////////////////////////////////////////////////////////////
34 Type
arithOpResult(Type t1
, Type t2
) {
35 if (!t1
.isKnownDataType() || !t2
.isKnownDataType()) {
40 if (both
.maybe(TDbl
)) return TDbl
;
41 if (both
.maybe(TArr
)) return TArr
;
42 if (both
.maybe(TVec
)) return TVec
;
43 if (both
.maybe(TDict
)) return TDict
;
44 if (both
.maybe(TKeyset
)) return TKeyset
;
45 if (both
.maybe(TClsMeth
)) return TClsMeth
;
46 if (both
.maybe(TStr
)) return TCell
;
50 Type
arithOpOverResult(Type t1
, Type t2
) {
51 if (t1
<= TInt
&& t2
<= TInt
) {
54 return arithOpResult(t1
, t2
);
57 Type
bitOpResult(Type t1
, Type t2
) {
58 if (!t1
.isKnownDataType() || !t2
.isKnownDataType()) {
63 if (both
<= TStr
) return TStr
;
67 Type
setOpResult(Type locType
, Type valType
, SetOpOp op
) {
69 case SetOpOp::PlusEqual
:
70 case SetOpOp::MinusEqual
:
71 case SetOpOp::MulEqual
: return arithOpResult(locType
, valType
);
72 case SetOpOp::PlusEqualO
:
73 case SetOpOp::MinusEqualO
:
74 case SetOpOp::MulEqualO
: return arithOpOverResult(locType
, valType
);
75 case SetOpOp::ConcatEqual
: return TStr
;
76 case SetOpOp::PowEqual
:
77 case SetOpOp::DivEqual
:
78 case SetOpOp::ModEqual
: return TUncountedInit
;
79 case SetOpOp::AndEqual
:
80 case SetOpOp::OrEqual
:
81 case SetOpOp::XorEqual
: return bitOpResult(locType
, valType
);
82 case SetOpOp::SlEqual
:
83 case SetOpOp::SrEqual
: return TInt
;
88 uint32_t localInputId(SrcKey sk
) {
89 auto const idx
= localImmIdx(sk
.op());
90 auto const argu
= getImm(sk
.pc(), idx
);
91 switch (immType(sk
.op(), idx
)) {
103 folly::Optional
<Type
> interpOutputType(IRGS
& env
,
104 folly::Optional
<Type
>& checkTypeType
) {
105 using namespace jit::InstrFlags
;
106 auto const sk
= curSrcKey(env
);
107 auto localType
= [&]{
108 auto locId
= localInputId(sk
);
109 static_assert(std::is_unsigned
<decltype(locId
)>::value
,
110 "locId should be unsigned");
111 assertx(locId
< curFunc(env
)->numLocals());
112 return env
.irb
->local(locId
, DataTypeSpecific
).type
;
115 switch (getInstrInfo(sk
.op()).type
) {
116 case OutNull
: return TInitNull
;
117 case OutNullUninit
: return TUninit
;
118 case OutString
: return TStr
;
119 case OutStringImm
: return TStaticStr
;
120 case OutDouble
: return TDbl
;
124 case OutBooleanImm
: return TBool
;
125 case OutInt64
: return TInt
;
126 case OutArrayImm
: return TArr
; // Should be StaticArr/Vec/Dict: t2124292
127 case OutVArray
: return RuntimeOption::EvalHackArrDVArrs
? TVec
: TVArr
;
128 case OutDArray
: return RuntimeOption::EvalHackArrDVArrs
? TDict
: TDArr
;
129 case OutVec
: return TVec
;
130 case OutVecImm
: return TVec
;
131 case OutDict
: return TDict
;
132 case OutDictImm
: return TDict
;
133 case OutKeyset
: return TKeyset
;
134 case OutKeysetImm
: return TKeyset
;
136 case OutThisObject
: return TObj
;
137 case OutRecord
: return TRecord
;
138 case OutResource
: return TRes
;
140 case OutFDesc
: return folly::none
;
141 case OutCns
: return TCell
;
143 case OutSameAsInput1
: return topType(env
, BCSPRelOffset
{0});
144 case OutSameAsInput2
: return topType(env
, BCSPRelOffset
{1});
145 case OutModifiedInput2
: return topType(env
, BCSPRelOffset
{1}).modified();
146 case OutModifiedInput3
: return topType(env
, BCSPRelOffset
{2}).modified();
149 return arithOpResult(topType(env
, BCSPRelOffset
{0}),
150 topType(env
, BCSPRelOffset
{1}));
152 return arithOpOverResult(topType(env
, BCSPRelOffset
{0}),
153 topType(env
, BCSPRelOffset
{1}));
154 case OutUnknown
: return TCell
;
157 return bitOpResult(topType(env
, BCSPRelOffset
{0}),
158 sk
.op() == HPHP::OpBitNot
?
159 TBottom
: topType(env
, BCSPRelOffset
{1}));
160 case OutSetOp
: return setOpResult(localType(),
161 topType(env
, BCSPRelOffset
{0}),
162 SetOpOp(getImm(sk
.pc(), 1).u_OA
));
164 auto ty
= localType();
165 return ty
<= TDbl
? ty
: TCell
;
167 case OutNone
: return folly::none
;
170 return topType(env
, BCSPRelOffset
{0});
174 auto ltype
= localType();
175 if (ltype
<= TCell
) return ltype
;
176 if (ltype
< TInitCell
) {
177 checkTypeType
= ltype
;
181 case OutFunc
: return TFunc
;
182 case OutFuncLike
: return TFuncLike
;
183 case OutClass
: return TCls
;
184 case OutClsMeth
: return TClsMeth
;
185 case OutClsMethLike
: return TClsMethLike
;
186 case OutLazyClass
: return TLazyCls
;
191 jit::vector
<InterpOneData::LocalType
>
192 interpOutputLocals(IRGS
& env
,
193 bool& smashesAllLocals
,
194 folly::Optional
<Type
> pushedType
) {
195 using namespace jit::InstrFlags
;
196 auto const sk
= curSrcKey(env
);
197 auto const& info
= getInstrInfo(sk
.op());
198 // Anything with Local in its output or a member base input can modify a
200 if (!(info
.out
& Local
) && !(info
.in
& MBase
)) return {};
202 jit::vector
<InterpOneData::LocalType
> locals
;
203 auto setLocType
= [&](uint32_t id
, Type t
) {
204 // Relax the type to something guardable. For InterpOne we don't bother to
205 // keep track of specialized types or inner-ref types. (And note that for
206 // psuedomains we may in fact have to guard on the local type after this.)
207 locals
.emplace_back(id
, relaxToGuardable(t
));
209 auto setImmLocType
= [&](uint32_t id
, Type t
) {
210 assertx(id
< kMaxHhbcImms
);
211 assertx(id
== localImmIdx(sk
.op()));
212 setLocType(localInputId(sk
), t
);
215 auto const mDefine
= static_cast<unsigned char>(MOpMode::Define
);
220 assertx(pushedType
.has_value());
222 auto stackType
= pushedType
.value();
223 setImmLocType(0, stackType
);
229 auto stackType
= topType(env
, BCSPRelOffset
{0});
230 setImmLocType(0, stackType
);
236 setImmLocType(0, TUninit
);
239 // New minstrs are handled extremely conservatively.
247 if (getImm(sk
.pc(), 0).u_OA
& mDefine
) smashesAllLocals
= true;
253 smashesAllLocals
= true;
260 auto const ita
= getImm(sk
.pc(), 0).u_ITA
;
261 setLocType(ita
.valId
, TCell
);
262 if (ita
.hasKey()) setLocType(ita
.keyId
, TCell
);
266 case OpVerifyParamTypeTS
:
267 case OpVerifyParamType
: {
268 setImmLocType(0, TCell
);
273 if (static_cast<SilenceOp
>(getImm(sk
.pc(), 1).u_OA
) == SilenceOp::Start
) {
274 setImmLocType(0, TInt
);
280 false, "Unknown local-modifying op {}", opcodeToName(sk
.op())
287 //////////////////////////////////////////////////////////////////////
291 void interpOne(IRGS
& env
) {
292 folly::Optional
<Type
> checkTypeType
;
293 auto const sk
= curSrcKey(env
);
294 auto stackType
= interpOutputType(env
, checkTypeType
);
295 auto popped
= getStackPopped(sk
.pc());
296 auto pushed
= getStackPushed(sk
.pc());
297 FTRACE(1, "emitting InterpOne for {}, result = {}, popped {}, pushed {}\n",
298 instrToString(sk
.pc(), sk
.func()),
299 stackType
.has_value() ? stackType
->toString() : "<none>",
302 InterpOneData idata
{ spOffBCFromIRSP(env
) };
303 auto locals
= interpOutputLocals(env
, idata
.smashesAllLocals
, stackType
);
304 idata
.nChangedLocals
= locals
.size();
305 idata
.changedLocals
= locals
.data();
307 interpOne(env
, stackType
, popped
, pushed
, idata
);
309 auto const out
= getInstrInfo(sk
.op()).out
;
310 auto const checkIdx
= BCSPRelOffset
{
311 (out
& InstrFlags::StackIns1
) ? 1 : 0
312 }.to
<FPInvOffset
>(env
.irb
->fs().bcSPOff());
314 auto const loc
= Location::Stack
{ checkIdx
};
315 checkType(env
, loc
, *checkTypeType
, nextBcOff(env
));
319 void interpOne(IRGS
& env
, int popped
) {
320 InterpOneData idata
{ spOffBCFromIRSP(env
) };
321 interpOne(env
, folly::none
, popped
, 0, idata
);
324 void interpOne(IRGS
& env
, Type outType
, int popped
) {
325 InterpOneData idata
{ spOffBCFromIRSP(env
) };
326 interpOne(env
, outType
, popped
, 1, idata
);
329 void interpOne(IRGS
& env
,
330 folly::Optional
<Type
> outType
,
333 InterpOneData
& idata
) {
334 auto const func
= curFunc(env
);
335 auto const op
= func
->getOp(bcOff(env
));
337 idata
.bcOff
= bcOff(env
);
338 idata
.cellsPopped
= popped
;
339 idata
.cellsPushed
= pushed
;
344 opcodeChangesPC(idata
.opcode
) ? InterpOneCF
: InterpOne
,
352 //////////////////////////////////////////////////////////////////////
355 * Instructions that unconditionally are implemented with InterpOne are
359 void emitExit(IRGS
& env
) { interpOne(env
); }
360 void emitFatal(IRGS
& env
, FatalOp
) { interpOne(env
); }
361 void emitSetOpG(IRGS
& env
, SetOpOp
) { interpOne(env
); }
362 void emitIncDecG(IRGS
& env
, IncDecOp
) { interpOne(env
); }
363 void emitUnsetG(IRGS
& env
) { interpOne(env
); }
364 void emitIncl(IRGS
& env
) { interpOne(env
); }
365 void emitInclOnce(IRGS
& env
) { interpOne(env
); }
366 void emitReq(IRGS
& env
) { interpOne(env
); }
367 void emitReqDoc(IRGS
& env
) { interpOne(env
); }
368 void emitReqOnce(IRGS
& env
) { interpOne(env
); }
369 void emitEval(IRGS
& env
) { interpOne(env
); }
370 void emitChainFaults(IRGS
& env
) { interpOne(env
); }
371 void emitContGetReturn(IRGS
& env
) { interpOne(env
); }
372 void emitResolveClass(IRGS
& env
, const StringData
*)
375 //////////////////////////////////////////////////////////////////////