Add support for HHBC ops with 5 immediates
[hiphop-php.git] / hphp / runtime / vm / jit / irgen-interpone.cpp
blobef761e4f2ff876252f97323f7798c571bf152648
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(TVec)) return TVec;
43 if (both.maybe(TDict)) return TDict;
44 if (both.maybe(TKeyset)) return TKeyset;
45 if (both.maybe(TStr)) return TCell;
46 return TInt;
49 Type arithOpOverResult(Type t1, Type t2) {
50 if (t1 <= TInt && t2 <= TInt) {
51 return TInt | TDbl;
53 return arithOpResult(t1, t2);
56 Type bitOpResult(Type t1, Type t2) {
57 if (!t1.isKnownDataType() || !t2.isKnownDataType()) {
58 return TCell;
61 auto both = t1 | t2;
62 if (both <= TStr) return TStr;
63 return TInt;
66 Type setOpResult(Type locType, Type valType, SetOpOp op) {
67 switch (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;
84 not_reached();
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;
95 auto localType = [&]{
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;
117 switch (outFlag) {
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;
123 case OutIsTypeL:
124 case OutBoolean:
125 case OutPredBool:
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;
136 case OutObject:
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 OutModifiedInput2: return topType(env, BCSPRelOffset{1}).modified();
146 case OutModifiedInput3: return topType(env, BCSPRelOffset{2}).modified();
147 case OutVInput: return boxed(topType(env, BCSPRelOffset{0}));
148 case OutVInputL: return boxed(localType());
149 case OutFInputL:
150 case OutFInputR: not_reached();
152 case OutArith:
153 return arithOpResult(topType(env, BCSPRelOffset{0}),
154 topType(env, BCSPRelOffset{1}));
155 case OutArithO:
156 return arithOpOverResult(topType(env, BCSPRelOffset{0}),
157 topType(env, BCSPRelOffset{1}));
158 case OutUnknown: {
159 if (isFPassStar(inst.op())) {
160 return inst.preppedByRef ? TBoxedInitCell : TCell;
162 return TGen;
164 case OutBitOp:
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));
171 case OutIncDec: {
172 auto ty = localType().unbox();
173 return ty <= TDbl ? ty : TCell;
175 case OutNone: return folly::none;
177 case OutCInput: {
178 auto ttype = topType(env, BCSPRelOffset{0});
179 if (ttype <= TCell) return ttype;
180 // All instructions that are OutCInput or OutCInputL cannot push uninit or
181 // a ref, so only specific inner types need to be checked.
182 if (ttype.unbox() < TInitCell) {
183 checkTypeType = ttype.unbox();
185 return TCell;
188 case OutCInputL: {
189 auto ltype = localType();
190 if (ltype <= TCell) return ltype;
191 if (ltype.unbox() < TInitCell) {
192 checkTypeType = ltype.unbox();
194 return TCell;
197 not_reached();
200 jit::vector<InterpOneData::LocalType>
201 interpOutputLocals(IRGS& env,
202 const NormalizedInstruction& inst,
203 bool& smashesAllLocals,
204 folly::Optional<Type> pushedType) {
205 using namespace jit::InstrFlags;
206 auto const& info = getInstrInfo(inst.op());
207 // Anything with Local in its output or a member base input can modify a
208 // local.
209 if (!(info.out & Local) && !(info.in & MBase)) return {};
211 jit::vector<InterpOneData::LocalType> locals;
212 auto setLocType = [&](uint32_t id, Type t) {
213 // Relax the type to something guardable. For InterpOne we don't bother to
214 // keep track of specialized types or inner-ref types. (And note that for
215 // psuedomains we may in fact have to guard on the local type after this.)
216 locals.emplace_back(id, relaxToGuardable(t));
218 auto setImmLocType = [&](uint32_t id, Type t) {
219 assertx(id < kMaxHhbcImms);
220 setLocType(inst.imm[id].u_LA, t);
222 auto handleBoxiness = [&] (Type testTy, Type useTy) {
223 return testTy <= TBoxedCell ? TBoxedInitCell :
224 testTy.maybe(TBoxedCell) ? TGen :
225 useTy;
228 auto const mDefine = static_cast<unsigned char>(MOpMode::Define);
230 switch (inst.op()) {
231 case OpSetN:
232 case OpSetOpN:
233 case OpIncDecN:
234 case OpBindN:
235 case OpVGetN:
236 case OpUnsetN:
237 smashesAllLocals = true;
238 break;
240 case OpSetOpL:
241 case OpIncDecL: {
242 assertx(pushedType.hasValue());
243 auto locType = env.irb->local(localInputId(inst), DataTypeSpecific).type;
244 assertx(locType < TGen || curFunc(env)->isPseudoMain());
246 auto stackType = pushedType.value();
247 setImmLocType(0, handleBoxiness(locType, stackType));
248 break;
251 case OpStaticLocInit:
252 case OpStaticLocDef:
253 setImmLocType(0, TBoxedInitCell);
254 break;
256 case OpStaticLocCheck:
257 setImmLocType(0, TGen);
258 break;
260 case OpInitThisLoc:
261 setImmLocType(0, TCell);
262 break;
264 case OpSetL:
265 case OpPopL: {
266 auto locType = env.irb->local(localInputId(inst), DataTypeSpecific).type;
267 auto stackType = topType(env, BCSPRelOffset{0});
268 // [Set,Pop]L preserves reffiness of a local.
269 setImmLocType(0, handleBoxiness(locType, stackType));
270 break;
272 case OpVGetL:
273 case OpBindL: {
274 assertx(pushedType.hasValue());
275 assertx(*pushedType <= TBoxedCell);
276 setImmLocType(0, pushedType.value());
277 break;
280 case OpUnsetL:
281 case OpPushL:
282 setImmLocType(0, TUninit);
283 break;
285 // New minstrs are handled extremely conservatively.
286 case OpQueryM:
287 case OpMemoGet:
288 break;
289 case OpDim:
290 if (inst.imm[0].u_OA & mDefine) smashesAllLocals = true;
291 break;
292 case OpFPassDim:
293 case OpFPassM:
294 case OpVGetM:
295 case OpSetM:
296 case OpIncDecM:
297 case OpSetOpM:
298 case OpBindM:
299 case OpUnsetM:
300 case OpSetWithRefLML:
301 case OpSetWithRefRML:
302 case OpMemoSet:
303 smashesAllLocals = true;
304 break;
306 case OpMIterInitK:
307 case OpMIterNextK:
308 setImmLocType(3, TCell);
309 /* fallthrough */
310 case OpMIterInit:
311 case OpMIterNext:
312 setImmLocType(2, TBoxedInitCell);
313 break;
315 case OpIterInitK:
316 case OpWIterInitK:
317 case OpIterNextK:
318 case OpWIterNextK:
319 setImmLocType(3, TCell);
320 /* fallthrough */
321 case OpIterInit:
322 case OpWIterInit:
323 case OpIterNext:
324 case OpWIterNext:
325 setImmLocType(2, TGen);
326 break;
328 case OpVerifyParamType: {
329 auto locType = env.irb->local(localInputId(inst), DataTypeSpecific).type;
330 setImmLocType(0, handleBoxiness(locType, TCell));
331 break;
334 case OpSilence:
335 if (static_cast<SilenceOp>(inst.imm[1].u_OA) == SilenceOp::Start) {
336 setImmLocType(0, TInt);
338 break;
340 default:
341 always_assert_flog(
342 false, "Unknown local-modifying op {}", opcodeToName(inst.op())
346 return locals;
349 jit::vector<InterpOneData::ClsRefSlot>
350 interpClsRefSlots(IRGS& /*env*/, const NormalizedInstruction& inst) {
351 jit::vector<InterpOneData::ClsRefSlot> slots;
353 auto const op = inst.op();
354 auto const numImmeds = numImmediates(op);
355 for (auto i = uint32_t{0}; i < numImmeds; ++i) {
356 auto const type = immType(op, i);
357 if (type == ArgType::CAR) {
358 slots.emplace_back(inst.imm[i].u_CAR, false);
359 } else if (type == ArgType::CAW) {
360 slots.emplace_back(inst.imm[i].u_CAW, true);
364 return slots;
367 //////////////////////////////////////////////////////////////////////
371 void interpOne(IRGS& env, const NormalizedInstruction& inst) {
372 folly::Optional<Type> checkTypeType;
373 auto stackType = interpOutputType(env, inst, checkTypeType);
374 auto popped = getStackPopped(inst.pc());
375 auto pushed = getStackPushed(inst.pc());
376 FTRACE(1, "emitting InterpOne for {}, result = {}, popped {}, pushed {}\n",
377 inst.toString(),
378 stackType.hasValue() ? stackType->toString() : "<none>",
379 popped, pushed);
381 InterpOneData idata { spOffBCFromIRSP(env) };
382 auto locals = interpOutputLocals(env, inst, idata.smashesAllLocals,
383 stackType);
384 idata.nChangedLocals = locals.size();
385 idata.changedLocals = locals.data();
387 auto slots = interpClsRefSlots(env, inst);
388 idata.nChangedClsRefSlots = slots.size();
389 idata.changedClsRefSlots = slots.data();
391 interpOne(env, stackType, popped, pushed, idata);
392 if (checkTypeType) {
393 auto const out = getInstrInfo(inst.op()).out;
394 auto const checkIdx = BCSPRelOffset{
395 (out & InstrFlags::StackIns1) ? 1 : 0
396 }.to<FPInvOffset>(env.irb->fs().bcSPOff());
398 checkType(env, Location::Stack { checkIdx }, *checkTypeType,
399 inst.nextSk().offset(), true /* outerOnly */);
403 void interpOne(IRGS& env, int popped) {
404 InterpOneData idata { spOffBCFromIRSP(env) };
405 interpOne(env, folly::none, popped, 0, idata);
408 void interpOne(IRGS& env, Type outType, int popped) {
409 InterpOneData idata { spOffBCFromIRSP(env) };
410 interpOne(env, outType, popped, 1, idata);
413 void interpOne(IRGS& env,
414 folly::Optional<Type> outType,
415 int popped,
416 int pushed,
417 InterpOneData& idata) {
418 auto const unit = curUnit(env);
419 auto const op = unit->getOp(bcOff(env));
421 idata.bcOff = bcOff(env);
422 idata.cellsPopped = popped;
423 idata.cellsPushed = pushed;
424 idata.opcode = op;
426 gen(
427 env,
428 opcodeChangesPC(idata.opcode) ? InterpOneCF : InterpOne,
429 outType,
430 idata,
431 sp(env),
432 fp(env)
436 //////////////////////////////////////////////////////////////////////
439 * Instructions that unconditionally are implemented with InterpOne are
440 * translated here.
443 #define INTERP interpOne(env, *env.currentNormalizedInstruction);
445 void emitFPushObjMethod(IRGS& env, uint32_t, ObjMethodOp, const ImmVector&) {
446 INTERP
449 void emitAddElemV(IRGS& env) { INTERP }
450 void emitAddNewElemV(IRGS& env) { INTERP }
451 void emitExit(IRGS& env) { INTERP }
452 void emitFatal(IRGS& env, FatalOp) { INTERP }
453 void emitUnwind(IRGS& env) { INTERP }
454 void emitThrow(IRGS& env) { INTERP }
455 void emitCGetN(IRGS& env) { INTERP }
456 void emitCGetQuietN(IRGS& env) { INTERP }
457 void emitVGetN(IRGS& env) { INTERP }
458 void emitIssetN(IRGS& env) { INTERP }
459 void emitEmptyN(IRGS& env) { INTERP }
460 void emitSetN(IRGS& env) { INTERP }
461 void emitSetOpN(IRGS& env, SetOpOp) { INTERP }
462 void emitSetOpG(IRGS& env, SetOpOp) { INTERP }
463 void emitSetOpS(IRGS& env, SetOpOp, uint32_t) { INTERP }
464 void emitIncDecN(IRGS& env, IncDecOp) { INTERP }
465 void emitIncDecG(IRGS& env, IncDecOp) { INTERP }
466 void emitBindN(IRGS& env) { INTERP }
467 void emitUnsetN(IRGS& env) { INTERP }
468 void emitUnsetG(IRGS& env) { INTERP }
469 void emitFPassN(IRGS& env,uint32_t,FPassHint) { INTERP }
470 void emitIncl(IRGS& env) { INTERP }
471 void emitInclOnce(IRGS& env) { INTERP }
472 void emitReq(IRGS& env) { INTERP }
473 void emitReqDoc(IRGS& env) { INTERP }
474 void emitReqOnce(IRGS& env) { INTERP }
475 void emitEval(IRGS& env) { INTERP }
476 void emitDefTypeAlias(IRGS& env, uint32_t) { INTERP }
477 void emitDefCns(IRGS& env, const StringData*) { INTERP }
478 void emitDefCls(IRGS& env, uint32_t) { INTERP }
479 void emitAliasCls(IRGS& env,
480 const StringData*,
481 const StringData*) { INTERP }
482 void emitDefFunc(IRGS& env, uint32_t) { INTERP }
483 void emitCatch(IRGS& env) { INTERP }
484 void emitChainFaults(IRGS& env) { INTERP }
485 void emitContGetReturn(IRGS& env) { INTERP }
486 void emitContAssignDelegate(IRGS& env, int32_t)
487 { INTERP }
488 void emitContEnterDelegate(IRGS& env) { INTERP }
489 void emitYieldFromDelegate(IRGS& env, int32_t, int32_t)
490 { INTERP }
491 void emitContUnsetDelegate(IRGS& env, CudOp, int32_t)
492 { INTERP }
494 //////////////////////////////////////////////////////////////////////