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(TShape
)) return TShape
;
43 if (both
.maybe(TVec
)) return TVec
;
44 if (both
.maybe(TDict
)) return TDict
;
45 if (both
.maybe(TKeyset
)) return TKeyset
;
46 if (both
.maybe(TClsMeth
)) return TClsMeth
;
47 if (both
.maybe(TStr
)) return TCell
;
51 Type
arithOpOverResult(Type t1
, Type t2
) {
52 if (t1
<= TInt
&& t2
<= TInt
) {
55 return arithOpResult(t1
, t2
);
58 Type
bitOpResult(Type t1
, Type t2
) {
59 if (!t1
.isKnownDataType() || !t2
.isKnownDataType()) {
64 if (both
<= TStr
) return TStr
;
68 Type
setOpResult(Type locType
, Type valType
, SetOpOp op
) {
70 case SetOpOp::PlusEqual
:
71 case SetOpOp::MinusEqual
:
72 case SetOpOp::MulEqual
: return arithOpResult(locType
.unbox(), valType
);
73 case SetOpOp::PlusEqualO
:
74 case SetOpOp::MinusEqualO
:
75 case SetOpOp::MulEqualO
: return arithOpOverResult(locType
.unbox(), valType
);
76 case SetOpOp::ConcatEqual
: return TStr
;
77 case SetOpOp::PowEqual
:
78 case SetOpOp::DivEqual
:
79 case SetOpOp::ModEqual
: return TUncountedInit
;
80 case SetOpOp::AndEqual
:
81 case SetOpOp::OrEqual
:
82 case SetOpOp::XorEqual
: return bitOpResult(locType
.unbox(), valType
);
83 case SetOpOp::SlEqual
:
84 case SetOpOp::SrEqual
: return TInt
;
89 uint32_t localInputId(const NormalizedInstruction
& inst
) {
90 return inst
.imm
[localImmIdx(inst
.op())].u_LA
;
93 folly::Optional
<Type
> interpOutputType(IRGS
& env
,
94 const NormalizedInstruction
& inst
,
95 folly::Optional
<Type
>& checkTypeType
) {
96 using namespace jit::InstrFlags
;
98 auto locId
= localInputId(inst
);
99 static_assert(std::is_unsigned
<decltype(locId
)>::value
,
100 "locId should be unsigned");
101 assertx(locId
< curFunc(env
)->numLocals());
102 return env
.irb
->local(locId
, DataTypeSpecific
).type
;
105 auto boxed
= [&] (Type t
) -> Type
{
106 if (t
== TGen
) return TBoxedInitCell
;
107 assertx(t
<= TCell
|| t
<= TBoxedCell
);
108 checkTypeType
= t
<= TBoxedCell
? t
: boxType(t
);
109 return TBoxedInitCell
;
112 switch (getInstrInfo(inst
.op()).type
) {
113 case OutNull
: return TInitNull
;
114 case OutNullUninit
: return TUninit
;
115 case OutString
: return TStr
;
116 case OutStringImm
: return TStaticStr
;
117 case OutDouble
: return TDbl
;
121 case OutBooleanImm
: return TBool
;
122 case OutInt64
: return TInt
;
123 case OutArray
: return TArr
;
124 case OutArrayImm
: return TArr
; // Should be StaticArr/Vec/Dict: t2124292
125 case OutVArray
: return RuntimeOption::EvalHackArrDVArrs
? TVec
: TArr
;
126 case OutDArray
: return RuntimeOption::EvalHackArrDVArrs
? TDict
: TArr
;
127 case OutVec
: return TVec
;
128 case OutVecImm
: return TVec
;
129 case OutDict
: return TDict
;
130 case OutDictImm
: return TDict
;
131 case OutKeyset
: return TKeyset
;
132 case OutKeysetImm
: return TKeyset
;
134 case OutThisObject
: return TObj
;
135 case OutResource
: return TRes
;
137 case OutFDesc
: return folly::none
;
138 case OutCns
: return TCell
;
139 case OutVUnknown
: return TBoxedInitCell
;
141 case OutSameAsInput1
: return topType(env
, BCSPRelOffset
{0});
142 case OutSameAsInput2
: return topType(env
, BCSPRelOffset
{1});
143 case OutModifiedInput2
: return topType(env
, BCSPRelOffset
{1}).modified();
144 case OutModifiedInput3
: return topType(env
, BCSPRelOffset
{2}).modified();
145 case OutVInput
: return boxed(topType(env
, BCSPRelOffset
{0}));
146 case OutVInputL
: return boxed(localType());
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 TGen
;
157 return bitOpResult(topType(env
, BCSPRelOffset
{0}),
158 inst
.op() == HPHP::OpBitNot
?
159 TBottom
: topType(env
, BCSPRelOffset
{1}));
160 case OutSetOp
: return setOpResult(localType(),
161 topType(env
, BCSPRelOffset
{0}),
162 SetOpOp(inst
.imm
[1].u_OA
));
164 auto ty
= localType().unbox();
165 return ty
<= TDbl
? ty
: TCell
;
167 case OutNone
: return folly::none
;
170 auto ttype
= topType(env
, BCSPRelOffset
{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();
188 case OutFunc
: return TFunc
;
189 case OutClass
: return TCls
;
190 case OutClsMeth
: return TClsMeth
;
195 jit::vector
<InterpOneData::LocalType
>
196 interpOutputLocals(IRGS
& env
,
197 const NormalizedInstruction
& inst
,
198 bool& smashesAllLocals
,
199 folly::Optional
<Type
> pushedType
) {
200 using namespace jit::InstrFlags
;
201 auto const& info
= getInstrInfo(inst
.op());
202 // Anything with Local in its output or a member base input can modify a
204 if (!(info
.out
& Local
) && !(info
.in
& MBase
)) return {};
206 jit::vector
<InterpOneData::LocalType
> locals
;
207 auto setLocType
= [&](uint32_t id
, Type t
) {
208 // Relax the type to something guardable. For InterpOne we don't bother to
209 // keep track of specialized types or inner-ref types. (And note that for
210 // psuedomains we may in fact have to guard on the local type after this.)
211 locals
.emplace_back(id
, relaxToGuardable(t
));
213 auto setImmLocType
= [&](uint32_t id
, Type t
) {
214 assertx(id
< kMaxHhbcImms
);
215 setLocType(inst
.imm
[id
].u_LA
, t
);
217 auto handleBoxiness
= [&] (Type testTy
, Type useTy
) {
218 return testTy
<= TBoxedCell
? TBoxedInitCell
:
219 testTy
.maybe(TBoxedCell
) ? TGen
:
223 auto const mDefine
= static_cast<unsigned char>(MOpMode::Define
);
228 assertx(pushedType
.hasValue());
229 auto locType
= env
.irb
->local(localInputId(inst
), DataTypeSpecific
).type
;
230 assertx(locType
< TGen
|| curFunc(env
)->isPseudoMain());
232 auto stackType
= pushedType
.value();
233 setImmLocType(0, handleBoxiness(locType
, stackType
));
238 setImmLocType(0, TCell
);
243 auto locType
= env
.irb
->local(localInputId(inst
), DataTypeSpecific
).type
;
244 auto stackType
= topType(env
, BCSPRelOffset
{0});
245 // [Set,Pop]L preserves reffiness of a local.
246 setImmLocType(0, handleBoxiness(locType
, stackType
));
250 assertx(pushedType
.hasValue());
251 assertx(*pushedType
<= TBoxedCell
);
252 setImmLocType(0, pushedType
.value());
258 setImmLocType(0, TUninit
);
261 // New minstrs are handled extremely conservatively.
269 if (inst
.imm
[0].u_OA
& mDefine
) smashesAllLocals
= true;
276 smashesAllLocals
= true;
281 setImmLocType(3, TCell
);
285 setImmLocType(2, TGen
);
290 setImmLocType(4, TCell
);
294 setImmLocType(3, TGen
);
297 case OpVerifyParamTypeTS
:
298 case OpVerifyParamType
: {
299 auto locType
= env
.irb
->local(localInputId(inst
), DataTypeSpecific
).type
;
300 setImmLocType(0, handleBoxiness(locType
, TCell
));
305 if (static_cast<SilenceOp
>(inst
.imm
[1].u_OA
) == SilenceOp::Start
) {
306 setImmLocType(0, TInt
);
312 false, "Unknown local-modifying op {}", opcodeToName(inst
.op())
319 jit::vector
<InterpOneData::ClsRefSlot
>
320 interpClsRefSlots(IRGS
& /*env*/, const NormalizedInstruction
& inst
) {
321 jit::vector
<InterpOneData::ClsRefSlot
> slots
;
323 auto const op
= inst
.op();
324 auto const numImmeds
= numImmediates(op
);
325 for (auto i
= uint32_t{0}; i
< numImmeds
; ++i
) {
326 auto const type
= immType(op
, i
);
327 if (type
== ArgType::CAR
) {
328 slots
.emplace_back(inst
.imm
[i
].u_CAR
, false);
329 } else if (type
== ArgType::CAW
) {
330 slots
.emplace_back(inst
.imm
[i
].u_CAW
, true);
337 //////////////////////////////////////////////////////////////////////
341 void interpOne(IRGS
& env
, const NormalizedInstruction
& inst
) {
342 folly::Optional
<Type
> checkTypeType
;
343 auto stackType
= interpOutputType(env
, inst
, checkTypeType
);
344 auto popped
= getStackPopped(inst
.pc());
345 auto pushed
= getStackPushed(inst
.pc());
346 FTRACE(1, "emitting InterpOne for {}, result = {}, popped {}, pushed {}\n",
348 stackType
.hasValue() ? stackType
->toString() : "<none>",
351 InterpOneData idata
{ spOffBCFromIRSP(env
) };
352 auto locals
= interpOutputLocals(env
, inst
, idata
.smashesAllLocals
,
354 idata
.nChangedLocals
= locals
.size();
355 idata
.changedLocals
= locals
.data();
357 auto slots
= interpClsRefSlots(env
, inst
);
358 idata
.nChangedClsRefSlots
= slots
.size();
359 idata
.changedClsRefSlots
= slots
.data();
361 interpOne(env
, stackType
, popped
, pushed
, idata
);
363 auto const out
= getInstrInfo(inst
.op()).out
;
364 auto const checkIdx
= BCSPRelOffset
{
365 (out
& InstrFlags::StackIns1
) ? 1 : 0
366 }.to
<FPInvOffset
>(env
.irb
->fs().bcSPOff());
368 checkType(env
, Location::Stack
{ checkIdx
}, *checkTypeType
,
369 inst
.nextSk().offset(), true /* outerOnly */);
373 void interpOne(IRGS
& env
, int popped
) {
374 InterpOneData idata
{ spOffBCFromIRSP(env
) };
375 interpOne(env
, folly::none
, popped
, 0, idata
);
378 void interpOne(IRGS
& env
, Type outType
, int popped
) {
379 InterpOneData idata
{ spOffBCFromIRSP(env
) };
380 interpOne(env
, outType
, popped
, 1, idata
);
383 void interpOne(IRGS
& env
,
384 folly::Optional
<Type
> outType
,
387 InterpOneData
& idata
) {
388 auto const unit
= curUnit(env
);
389 auto const op
= unit
->getOp(bcOff(env
));
391 idata
.bcOff
= bcOff(env
);
392 idata
.cellsPopped
= popped
;
393 idata
.cellsPushed
= pushed
;
398 opcodeChangesPC(idata
.opcode
) ? InterpOneCF
: InterpOne
,
406 //////////////////////////////////////////////////////////////////////
409 * Instructions that unconditionally are implemented with InterpOne are
413 #define INTERP interpOne(env, *env.currentNormalizedInstruction);
415 void emitFPushObjMethod(IRGS
& env
, uint32_t, ObjMethodOp
, const ImmVector
&) {
419 void emitAddElemV(IRGS
& env
) { INTERP
}
420 void emitAddNewElemV(IRGS
& env
) { INTERP
}
421 void emitExit(IRGS
& env
) { INTERP
}
422 void emitFatal(IRGS
& env
, FatalOp
) { INTERP
}
423 void emitSetOpG(IRGS
& env
, SetOpOp
) { INTERP
}
424 void emitSetOpS(IRGS
& env
, SetOpOp
, uint32_t) { INTERP
}
425 void emitIncDecG(IRGS
& env
, IncDecOp
) { INTERP
}
426 void emitUnsetG(IRGS
& env
) { INTERP
}
427 void emitVGetS(IRGS
& env
, uint32_t) { INTERP
}
428 void emitIncl(IRGS
& env
) { INTERP
}
429 void emitInclOnce(IRGS
& env
) { INTERP
}
430 void emitReq(IRGS
& env
) { INTERP
}
431 void emitReqDoc(IRGS
& env
) { INTERP
}
432 void emitReqOnce(IRGS
& env
) { INTERP
}
433 void emitEval(IRGS
& env
) { INTERP
}
434 void emitDefTypeAlias(IRGS
& env
, uint32_t) { INTERP
}
435 void emitDefCns(IRGS
& env
, const StringData
*) { INTERP
}
436 void emitDefCls(IRGS
& env
, uint32_t) { INTERP
}
437 void emitAliasCls(IRGS
& env
,
439 const StringData
*) { INTERP
}
440 void emitChainFaults(IRGS
& env
) { INTERP
}
441 void emitContGetReturn(IRGS
& env
) { INTERP
}
442 void emitContAssignDelegate(IRGS
& env
, int32_t)
444 void emitContEnterDelegate(IRGS
& env
) { INTERP
}
445 void emitYieldFromDelegate(IRGS
& env
, int32_t, int32_t)
447 void emitContUnsetDelegate(IRGS
& env
, CudOp
, int32_t)
450 //////////////////////////////////////////////////////////////////////