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/jit/location.h"
22 #include "hphp/runtime/vm/jit/minstr-effects.h"
23 #include "hphp/runtime/vm/jit/normalized-instruction.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(TStr
)) return TCell
;
49 Type
arithOpOverResult(Type t1
, Type t2
) {
50 if (t1
<= TInt
&& t2
<= TInt
) {
53 return arithOpResult(t1
, t2
);
56 Type
bitOpResult(Type t1
, Type t2
) {
57 if (!t1
.isKnownDataType() || !t2
.isKnownDataType()) {
62 if (both
<= TStr
) return TStr
;
66 Type
setOpResult(Type locType
, Type valType
, SetOpOp op
) {
68 case SetOpOp::PlusEqual
:
69 case SetOpOp::MinusEqual
:
70 case SetOpOp::MulEqual
: return arithOpResult(locType
.unbox(), valType
);
71 case SetOpOp::PlusEqualO
:
72 case SetOpOp::MinusEqualO
:
73 case SetOpOp::MulEqualO
: return arithOpOverResult(locType
.unbox(), valType
);
74 case SetOpOp::ConcatEqual
: return TStr
;
75 case SetOpOp::PowEqual
:
76 case SetOpOp::DivEqual
:
77 case SetOpOp::ModEqual
: return TUncountedInit
;
78 case SetOpOp::AndEqual
:
79 case SetOpOp::OrEqual
:
80 case SetOpOp::XorEqual
: return bitOpResult(locType
.unbox(), valType
);
81 case SetOpOp::SlEqual
:
82 case SetOpOp::SrEqual
: return TInt
;
87 uint32_t localInputId(const NormalizedInstruction
& inst
) {
88 return inst
.imm
[localImmIdx(inst
.op())].u_LA
;
91 folly::Optional
<Type
> interpOutputType(IRGS
& env
,
92 const NormalizedInstruction
& inst
,
93 folly::Optional
<Type
>& checkTypeType
) {
94 using namespace jit::InstrFlags
;
96 auto locId
= localInputId(inst
);
97 static_assert(std::is_unsigned
<decltype(locId
)>::value
,
98 "locId should be unsigned");
99 assertx(locId
< curFunc(env
)->numLocals());
100 return env
.irb
->local(locId
, DataTypeSpecific
).type
;
103 auto boxed
= [&] (Type t
) -> Type
{
104 if (t
== TGen
) return TBoxedInitCell
;
105 assertx(t
<= TCell
|| t
<= TBoxedCell
);
106 checkTypeType
= t
<= TBoxedCell
? t
: boxType(t
);
107 return TBoxedInitCell
;
110 auto outFlag
= getInstrInfo(inst
.op()).type
;
111 if (outFlag
== OutFInputL
) {
112 outFlag
= inst
.preppedByRef
? OutVInputL
: OutCInputL
;
113 } else if (outFlag
== OutFInputR
) {
114 outFlag
= inst
.preppedByRef
? OutVInput
: OutCInput
;
118 case OutNull
: return TInitNull
;
119 case OutNullUninit
: return TUninit
;
120 case OutString
: return TStr
;
121 case OutStringImm
: return TStaticStr
;
122 case OutDouble
: return TDbl
;
126 case OutBooleanImm
: return TBool
;
127 case OutInt64
: return TInt
;
128 case OutArray
: return TArr
;
129 case OutArrayImm
: return TArr
; // Should be StaticArr/Vec/Dict: t2124292
130 case OutVec
: return TVec
;
131 case OutVecImm
: return TVec
;
132 case OutDict
: return TDict
;
133 case OutDictImm
: return TDict
;
134 case OutKeyset
: return TKeyset
;
135 case OutKeysetImm
: return TKeyset
;
137 case OutThisObject
: return TObj
;
138 case OutResource
: return TRes
;
140 case OutFDesc
: return folly::none
;
141 case OutCns
: return TCell
;
142 case OutVUnknown
: return TBoxedInitCell
;
144 case OutSameAsInput1
: return topType(env
, BCSPRelOffset
{0});
145 case OutSameAsInput2
: return topType(env
, BCSPRelOffset
{1});
146 case OutSameAsInput3
: return topType(env
, BCSPRelOffset
{2});
147 case OutVInput
: return boxed(topType(env
, BCSPRelOffset
{0}));
148 case OutVInputL
: return boxed(localType());
150 case OutFInputR
: not_reached();
153 return arithOpResult(topType(env
, BCSPRelOffset
{0}),
154 topType(env
, BCSPRelOffset
{1}));
156 return arithOpOverResult(topType(env
, BCSPRelOffset
{0}),
157 topType(env
, BCSPRelOffset
{1}));
159 if (isFPassStar(inst
.op())) {
160 return inst
.preppedByRef
? TBoxedInitCell
: TCell
;
165 return bitOpResult(topType(env
, BCSPRelOffset
{0}),
166 inst
.op() == HPHP::OpBitNot
?
167 TBottom
: topType(env
, BCSPRelOffset
{1}));
168 case OutSetOp
: return setOpResult(localType(),
169 topType(env
, BCSPRelOffset
{0}),
170 SetOpOp(inst
.imm
[1].u_OA
));
172 auto ty
= localType().unbox();
173 return ty
<= TDbl
? ty
: TCell
;
175 case OutClassRef
: return TCls
;
176 case OutFPushCufSafe
: return folly::none
;
178 case OutNone
: return folly::none
;
181 auto ttype
= topType(env
, BCSPRelOffset
{0});
182 if (ttype
<= TCell
) return ttype
;
183 // All instructions that are OutCInput or OutCInputL cannot push uninit or
184 // a ref, so only specific inner types need to be checked.
185 if (ttype
.unbox() < TInitCell
) {
186 checkTypeType
= ttype
.unbox();
192 auto ltype
= localType();
193 if (ltype
<= TCell
) return ltype
;
194 if (ltype
.unbox() < TInitCell
) {
195 checkTypeType
= ltype
.unbox();
203 jit::vector
<InterpOneData::LocalType
>
204 interpOutputLocals(IRGS
& env
,
205 const NormalizedInstruction
& inst
,
206 bool& smashesAllLocals
,
207 folly::Optional
<Type
> pushedType
) {
208 using namespace jit::InstrFlags
;
209 auto const& info
= getInstrInfo(inst
.op());
210 // Anything with Local in its output or a member base input can modify a
212 if (!(info
.out
& Local
) && !(info
.in
& MBase
)) return {};
214 jit::vector
<InterpOneData::LocalType
> locals
;
215 auto setLocType
= [&](uint32_t id
, Type t
) {
216 // Relax the type to something guardable. For InterpOne we don't bother to
217 // keep track of specialized types or inner-ref types. (And note that for
218 // psuedomains we may in fact have to guard on the local type after this.)
219 locals
.emplace_back(id
, relaxToGuardable(t
));
221 auto setImmLocType
= [&](uint32_t id
, Type t
) {
222 setLocType(inst
.imm
[id
].u_LA
, t
);
224 auto handleBoxiness
= [&] (Type testTy
, Type useTy
) {
225 return testTy
<= TBoxedCell
? TBoxedInitCell
:
226 testTy
.maybe(TBoxedCell
) ? TGen
:
230 auto const mDefine
= static_cast<unsigned char>(MOpMode::Define
);
239 smashesAllLocals
= true;
244 assertx(pushedType
.hasValue());
245 auto locType
= env
.irb
->local(localInputId(inst
), DataTypeSpecific
).type
;
246 assertx(locType
< TGen
|| curFunc(env
)->isPseudoMain());
248 auto stackType
= pushedType
.value();
249 setImmLocType(0, handleBoxiness(locType
, stackType
));
253 case OpStaticLocInit
:
254 setImmLocType(0, TBoxedInitCell
);
258 setImmLocType(0, TCell
);
262 auto locType
= env
.irb
->local(localInputId(inst
), DataTypeSpecific
).type
;
263 auto stackType
= topType(env
, BCSPRelOffset
{0});
264 // SetL preserves reffiness of a local.
265 setImmLocType(0, handleBoxiness(locType
, stackType
));
270 assertx(pushedType
.hasValue());
271 assertx(*pushedType
<= TBoxedCell
);
272 setImmLocType(0, pushedType
.value());
278 setImmLocType(0, TUninit
);
281 // New minstrs are handled extremely conservatively.
285 if (inst
.imm
[0].u_OA
& mDefine
) smashesAllLocals
= true;
295 case OpSetWithRefLML
:
296 case OpSetWithRefRML
:
297 smashesAllLocals
= true;
302 setImmLocType(3, TCell
);
306 setImmLocType(2, TBoxedInitCell
);
313 setImmLocType(3, TCell
);
319 setImmLocType(2, TGen
);
322 case OpVerifyParamType
: {
323 auto locType
= env
.irb
->local(localInputId(inst
), DataTypeSpecific
).type
;
324 setImmLocType(0, handleBoxiness(locType
, TCell
));
329 if (static_cast<SilenceOp
>(inst
.imm
[0].u_OA
) == SilenceOp::Start
) {
330 setImmLocType(inst
.imm
[0].u_LA
, TInt
);
336 false, "Unknown local-modifying op {}", opcodeToName(inst
.op())
343 //////////////////////////////////////////////////////////////////////
347 void interpOne(IRGS
& env
, const NormalizedInstruction
& inst
) {
348 folly::Optional
<Type
> checkTypeType
;
349 auto stackType
= interpOutputType(env
, inst
, checkTypeType
);
350 auto popped
= getStackPopped(inst
.pc());
351 auto pushed
= getStackPushed(inst
.pc());
352 FTRACE(1, "emitting InterpOne for {}, result = {}, popped {}, pushed {}\n",
354 stackType
.hasValue() ? stackType
->toString() : "<none>",
357 InterpOneData idata
{ spOffBCFromIRSP(env
) };
358 auto locals
= interpOutputLocals(env
, inst
, idata
.smashesAllLocals
,
360 idata
.nChangedLocals
= locals
.size();
361 idata
.changedLocals
= locals
.data();
363 interpOne(env
, stackType
, popped
, pushed
, idata
);
365 auto const out
= getInstrInfo(inst
.op()).out
;
366 auto const checkIdx
= BCSPRelOffset
{
367 (out
& InstrFlags::StackIns2
) ? 2 :
368 (out
& InstrFlags::StackIns1
) ? 1 : 0
369 }.to
<FPInvOffset
>(env
.irb
->fs().bcSPOff());
371 checkType(env
, Location::Stack
{ checkIdx
}, *checkTypeType
,
372 inst
.nextSk().offset(), true /* outerOnly */);
376 void interpOne(IRGS
& env
, int popped
) {
377 InterpOneData idata
{ spOffBCFromIRSP(env
) };
378 interpOne(env
, folly::none
, popped
, 0, idata
);
381 void interpOne(IRGS
& env
, Type outType
, int popped
) {
382 InterpOneData idata
{ spOffBCFromIRSP(env
) };
383 interpOne(env
, outType
, popped
, 1, idata
);
386 void interpOne(IRGS
& env
,
387 folly::Optional
<Type
> outType
,
390 InterpOneData
& idata
) {
391 auto const unit
= curUnit(env
);
392 auto const op
= unit
->getOp(bcOff(env
));
394 idata
.bcOff
= bcOff(env
);
395 idata
.cellsPopped
= popped
;
396 idata
.cellsPushed
= pushed
;
401 opcodeChangesPC(idata
.opcode
) ? InterpOneCF
: InterpOne
,
409 //////////////////////////////////////////////////////////////////////
412 * Instructions that unconditionally are implemented with InterpOne are
416 #define INTERP interpOne(env, *env.currentNormalizedInstruction);
418 void emitFPushObjMethod(IRGS
& env
, int32_t, ObjMethodOp
) { INTERP
}
420 void emitLowInvalid(IRGS
& env
) { std::abort(); }
421 void emitCGetL3(IRGS
& env
, int32_t) { INTERP
}
422 void emitAddElemV(IRGS
& env
) { INTERP
}
423 void emitAddNewElemV(IRGS
& env
) { INTERP
}
424 void emitExit(IRGS
& env
) { INTERP
}
425 void emitFatal(IRGS
& env
, FatalOp
) { INTERP
}
426 void emitUnwind(IRGS
& env
) { INTERP
}
427 void emitThrow(IRGS
& env
) { INTERP
}
428 void emitCGetN(IRGS
& env
) { INTERP
}
429 void emitCGetQuietN(IRGS
& env
) { INTERP
}
430 void emitVGetN(IRGS
& env
) { INTERP
}
431 void emitIssetN(IRGS
& env
) { INTERP
}
432 void emitEmptyN(IRGS
& env
) { INTERP
}
433 void emitSetN(IRGS
& env
) { INTERP
}
434 void emitSetOpN(IRGS
& env
, SetOpOp
) { INTERP
}
435 void emitSetOpG(IRGS
& env
, SetOpOp
) { INTERP
}
436 void emitSetOpS(IRGS
& env
, SetOpOp
) { INTERP
}
437 void emitIncDecN(IRGS
& env
, IncDecOp
) { INTERP
}
438 void emitIncDecG(IRGS
& env
, IncDecOp
) { INTERP
}
439 void emitBindN(IRGS
& env
) { INTERP
}
440 void emitUnsetN(IRGS
& env
) { INTERP
}
441 void emitUnsetG(IRGS
& env
) { INTERP
}
442 void emitFPassN(IRGS
& env
, int32_t) { INTERP
}
443 void emitCufSafeArray(IRGS
& env
) { INTERP
}
444 void emitCufSafeReturn(IRGS
& env
) { INTERP
}
445 void emitIncl(IRGS
& env
) { INTERP
}
446 void emitInclOnce(IRGS
& env
) { INTERP
}
447 void emitReq(IRGS
& env
) { INTERP
}
448 void emitReqDoc(IRGS
& env
) { INTERP
}
449 void emitReqOnce(IRGS
& env
) { INTERP
}
450 void emitEval(IRGS
& env
) { INTERP
}
451 void emitDefTypeAlias(IRGS
& env
, int32_t) { INTERP
}
452 void emitDefCns(IRGS
& env
, const StringData
*) { INTERP
}
453 void emitDefCls(IRGS
& env
, int32_t) { INTERP
}
454 void emitDefFunc(IRGS
& env
, int32_t) { INTERP
}
455 void emitCatch(IRGS
& env
) { INTERP
}
456 void emitContGetReturn(IRGS
& env
) { INTERP
}
457 void emitContAssignDelegate(IRGS
& env
, int32_t)
459 void emitContEnterDelegate(IRGS
& env
) { INTERP
}
460 void emitYieldFromDelegate(IRGS
& env
, int32_t, int32_t)
462 void emitContUnsetDelegate(IRGS
& env
, int32_t, int32_t)
464 void emitHighInvalid(IRGS
& env
) { std::abort(); }
466 //////////////////////////////////////////////////////////////////////