Replace Catch opcode with implicitly pushed Throwables
[hiphop-php.git] / hphp / runtime / vm / jit / irgen-interpone.cpp
blob995a343c4296c25773e732667e485a1814b97306
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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"
19 #include <cstdlib>
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 {
30 namespace {
32 //////////////////////////////////////////////////////////////////////
34 Type arithOpResult(Type t1, Type t2) {
35 if (!t1.isKnownDataType() || !t2.isKnownDataType()) {
36 return TCell;
39 auto both = t1 | t2;
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;
48 return TInt;
51 Type arithOpOverResult(Type t1, Type t2) {
52 if (t1 <= TInt && t2 <= TInt) {
53 return TInt | TDbl;
55 return arithOpResult(t1, t2);
58 Type bitOpResult(Type t1, Type t2) {
59 if (!t1.isKnownDataType() || !t2.isKnownDataType()) {
60 return TCell;
63 auto both = t1 | t2;
64 if (both <= TStr) return TStr;
65 return TInt;
68 Type setOpResult(Type locType, Type valType, SetOpOp op) {
69 switch (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;
86 not_reached();
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;
97 auto localType = [&]{
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;
118 case OutIsTypeL:
119 case OutBoolean:
120 case OutPredBool:
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;
133 case OutObject:
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());
148 case OutArith:
149 return arithOpResult(topType(env, BCSPRelOffset{0}),
150 topType(env, BCSPRelOffset{1}));
151 case OutArithO:
152 return arithOpOverResult(topType(env, BCSPRelOffset{0}),
153 topType(env, BCSPRelOffset{1}));
154 case OutUnknown: return TGen;
156 case OutBitOp:
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));
163 case OutIncDec: {
164 auto ty = localType().unbox();
165 return ty <= TDbl ? ty : TCell;
167 case OutNone: return folly::none;
169 case OutCInput: {
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();
177 return TCell;
180 case OutCInputL: {
181 auto ltype = localType();
182 if (ltype <= TCell) return ltype;
183 if (ltype.unbox() < TInitCell) {
184 checkTypeType = ltype.unbox();
186 return TCell;
188 case OutFunc: return TFunc;
189 case OutClass: return TCls;
190 case OutClsMeth: return TClsMeth;
192 not_reached();
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
203 // local.
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 :
220 useTy;
223 auto const mDefine = static_cast<unsigned char>(MOpMode::Define);
225 switch (inst.op()) {
226 case OpSetOpL:
227 case OpIncDecL: {
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));
234 break;
237 case OpInitThisLoc:
238 setImmLocType(0, TCell);
239 break;
241 case OpSetL:
242 case OpPopL: {
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));
247 break;
249 case OpVGetL: {
250 assertx(pushedType.hasValue());
251 assertx(*pushedType <= TBoxedCell);
252 setImmLocType(0, pushedType.value());
253 break;
256 case OpUnsetL:
257 case OpPushL:
258 setImmLocType(0, TUninit);
259 break;
261 // New minstrs are handled extremely conservatively.
262 case OpQueryM:
263 case OpMemoGet:
264 case OpMemoGetEager:
265 case OpMemoSet:
266 case OpMemoSetEager:
267 break;
268 case OpDim:
269 if (inst.imm[0].u_OA & mDefine) smashesAllLocals = true;
270 break;
271 case OpVGetM:
272 case OpSetM:
273 case OpIncDecM:
274 case OpSetOpM:
275 case OpUnsetM:
276 smashesAllLocals = true;
277 break;
279 case OpIterInitK:
280 case OpIterNextK:
281 setImmLocType(3, TCell);
282 /* fallthrough */
283 case OpIterInit:
284 case OpIterNext:
285 setImmLocType(2, TGen);
286 break;
288 case OpLIterInitK:
289 case OpLIterNextK:
290 setImmLocType(4, TCell);
291 /* fallthrough */
292 case OpLIterInit:
293 case OpLIterNext:
294 setImmLocType(3, TGen);
295 break;
297 case OpVerifyParamTypeTS:
298 case OpVerifyParamType: {
299 auto locType = env.irb->local(localInputId(inst), DataTypeSpecific).type;
300 setImmLocType(0, handleBoxiness(locType, TCell));
301 break;
304 case OpSilence:
305 if (static_cast<SilenceOp>(inst.imm[1].u_OA) == SilenceOp::Start) {
306 setImmLocType(0, TInt);
308 break;
310 default:
311 always_assert_flog(
312 false, "Unknown local-modifying op {}", opcodeToName(inst.op())
316 return locals;
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);
334 return slots;
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",
347 inst.toString(),
348 stackType.hasValue() ? stackType->toString() : "<none>",
349 popped, pushed);
351 InterpOneData idata { spOffBCFromIRSP(env) };
352 auto locals = interpOutputLocals(env, inst, idata.smashesAllLocals,
353 stackType);
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);
362 if (checkTypeType) {
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,
385 int popped,
386 int pushed,
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;
394 idata.opcode = op;
396 gen(
397 env,
398 opcodeChangesPC(idata.opcode) ? InterpOneCF : InterpOne,
399 outType,
400 idata,
401 sp(env),
402 fp(env)
406 //////////////////////////////////////////////////////////////////////
409 * Instructions that unconditionally are implemented with InterpOne are
410 * translated here.
413 #define INTERP interpOne(env, *env.currentNormalizedInstruction);
415 void emitFPushObjMethod(IRGS& env, uint32_t, ObjMethodOp, const ImmVector&) {
416 INTERP
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,
438 const StringData*,
439 const StringData*) { INTERP }
440 void emitChainFaults(IRGS& env) { INTERP }
441 void emitContGetReturn(IRGS& env) { INTERP }
442 void emitContAssignDelegate(IRGS& env, int32_t)
443 { INTERP }
444 void emitContEnterDelegate(IRGS& env) { INTERP }
445 void emitYieldFromDelegate(IRGS& env, int32_t, int32_t)
446 { INTERP }
447 void emitContUnsetDelegate(IRGS& env, CudOp, int32_t)
448 { INTERP }
450 //////////////////////////////////////////////////////////////////////