Implement PHP7 null coalesce operator
[hiphop-php.git] / hphp / runtime / vm / jit / irgen-interpone.cpp
blobc947148e3c83d003e78a103cf2e537fa35816bc6
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2015 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 +----------------------------------------------------------------------+
16 #include "hphp/runtime/vm/jit/irgen-interpone.h"
18 #include <cstdlib>
20 #include "hphp/runtime/vm/jit/minstr-effects.h"
21 #include "hphp/runtime/vm/jit/normalized-instruction.h"
23 #include "hphp/runtime/vm/jit/irgen-exit.h"
24 #include "hphp/runtime/vm/jit/irgen-guards.h"
25 #include "hphp/runtime/vm/jit/irgen-internal.h"
27 namespace HPHP { namespace jit { namespace irgen {
29 namespace {
31 //////////////////////////////////////////////////////////////////////
33 Type arithOpResult(Type t1, Type t2) {
34 if (!t1.isKnownDataType() || !t2.isKnownDataType()) {
35 return TCell;
38 auto both = t1 | t2;
39 if (both.maybe(TDbl)) return TDbl;
40 if (both.maybe(TArr)) return TArr;
41 if (both.maybe(TStr)) return TCell;
42 return TInt;
45 Type arithOpOverResult(Type t1, Type t2) {
46 if (t1 <= TInt && t2 <= TInt) {
47 return TInt | TDbl;
49 return arithOpResult(t1, t2);
52 Type bitOpResult(Type t1, Type t2) {
53 if (!t1.isKnownDataType() || !t2.isKnownDataType()) {
54 return TCell;
57 auto both = t1 | t2;
58 if (both <= TStr) return TStr;
59 return TInt;
62 Type setOpResult(Type locType, Type valType, SetOpOp op) {
63 switch (op) {
64 case SetOpOp::PlusEqual:
65 case SetOpOp::MinusEqual:
66 case SetOpOp::MulEqual: return arithOpResult(locType.unbox(), valType);
67 case SetOpOp::PlusEqualO:
68 case SetOpOp::MinusEqualO:
69 case SetOpOp::MulEqualO: return arithOpOverResult(locType.unbox(), valType);
70 case SetOpOp::ConcatEqual: return TStr;
71 case SetOpOp::PowEqual:
72 case SetOpOp::DivEqual:
73 case SetOpOp::ModEqual: return TUncountedInit;
74 case SetOpOp::AndEqual:
75 case SetOpOp::OrEqual:
76 case SetOpOp::XorEqual: return bitOpResult(locType.unbox(), valType);
77 case SetOpOp::SlEqual:
78 case SetOpOp::SrEqual: return TInt;
80 not_reached();
83 uint32_t localInputId(const NormalizedInstruction& inst) {
84 switch (inst.op()) {
85 case OpSetWithRefLM:
86 case OpFPassL:
87 return inst.imm[1].u_LA;
89 default:
90 return inst.imm[0].u_LA;
94 folly::Optional<Type> interpOutputType(IRGS& env,
95 const NormalizedInstruction& inst,
96 folly::Optional<Type>& checkTypeType) {
97 using namespace jit::InstrFlags;
98 auto localType = [&]{
99 auto locId = localInputId(inst);
100 static_assert(std::is_unsigned<decltype(locId)>::value,
101 "locId should be unsigned");
102 assertx(locId < curFunc(env)->numLocals());
103 return env.irb->localType(locId, DataTypeSpecific);
106 auto boxed = [&] (Type t) -> Type {
107 if (t == TGen) return TBoxedInitCell;
108 assertx(t <= TCell || t <= TBoxedCell);
109 checkTypeType = t <= TBoxedCell ? t : boxType(t);
110 return TBoxedInitCell;
113 auto outFlag = getInstrInfo(inst.op()).type;
114 if (outFlag == OutFInputL) {
115 outFlag = inst.preppedByRef ? OutVInputL : OutCInputL;
116 } else if (outFlag == OutFInputR) {
117 outFlag = inst.preppedByRef ? OutVInput : OutCInput;
120 switch (outFlag) {
121 case OutNull: return TInitNull;
122 case OutNullUninit: return TUninit;
123 case OutString: return TStr;
124 case OutStringImm: return TStaticStr;
125 case OutDouble: return TDbl;
126 case OutIsTypeL:
127 case OutBoolean:
128 case OutPredBool:
129 case OutBooleanImm: return TBool;
130 case OutInt64: return TInt;
131 case OutArray: return TArr;
132 case OutArrayImm: return TArr; // Should be StaticArr: t2124292
133 case OutObject:
134 case OutThisObject: return TObj;
135 case OutResource: return TRes;
137 case OutFDesc: return folly::none;
138 case OutUnknown: return TGen;
140 case OutCns: return TCell;
141 case OutVUnknown: return TBoxedInitCell;
143 case OutSameAsInput: return topType(env, BCSPOffset{0});
144 case OutVInput: return boxed(topType(env, BCSPOffset{0}));
145 case OutVInputL: return boxed(localType());
146 case OutFInputL:
147 case OutFInputR: not_reached();
149 case OutArith: return arithOpResult(topType(env, BCSPOffset{0}),
150 topType(env, BCSPOffset{1}));
151 case OutArithO: return arithOpOverResult(topType(env, BCSPOffset{0}),
152 topType(env, BCSPOffset{1}));
153 case OutBitOp:
154 return bitOpResult(topType(env, BCSPOffset{0}),
155 inst.op() == HPHP::OpBitNot ?
156 TBottom : topType(env, BCSPOffset{1}));
157 case OutSetOp: return setOpResult(localType(),
158 topType(env, BCSPOffset{0}),
159 SetOpOp(inst.imm[1].u_OA));
160 case OutIncDec: {
161 auto ty = localType().unbox();
162 return ty <= TDbl ? ty : TCell;
164 case OutClassRef: return TCls;
165 case OutFPushCufSafe: return folly::none;
167 case OutNone: return folly::none;
169 case OutCInput: {
170 auto ttype = topType(env, BCSPOffset{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;
189 not_reached();
192 jit::vector<InterpOneData::LocalType>
193 interpOutputLocals(IRGS& env,
194 const NormalizedInstruction& inst,
195 bool& smashesAllLocals,
196 folly::Optional<Type> pushedType) {
197 using namespace jit::InstrFlags;
198 auto const& info = getInstrInfo(inst.op());
199 // Anything with Local in its output or a member base input can modify a
200 // local.
201 if (!(info.out & Local) && !(info.in & MBase)) return {};
203 jit::vector<InterpOneData::LocalType> locals;
204 auto setLocType = [&](uint32_t id, Type t) {
205 // Relax the type to something guardable. For InterpOne we don't bother to
206 // keep track of specialized types or inner-ref types. (And note that for
207 // psuedomains we may in fact have to guard on the local type after this.)
208 locals.emplace_back(id, relaxToGuardable(t));
210 auto setImmLocType = [&](uint32_t id, Type t) {
211 setLocType(inst.imm[id].u_LA, t);
213 auto const func = curFunc(env);
215 auto handleBoxiness = [&] (Type testTy, Type useTy) {
216 return testTy <= TBoxedCell ? TBoxedInitCell :
217 testTy.maybe(TBoxedCell) ? TGen :
218 useTy;
221 auto const mDefine = static_cast<unsigned char>(MOpFlags::Define);
223 switch (inst.op()) {
224 case OpSetN:
225 case OpSetOpN:
226 case OpIncDecN:
227 case OpBindN:
228 case OpVGetN:
229 case OpUnsetN:
230 smashesAllLocals = true;
231 break;
233 case OpFPassM:
234 switch (inst.immVec.locationCode()) {
235 case LL:
236 case LNL:
237 case LNC:
238 // FPassM may or may not affect local types, depending on whether it is
239 // passing to a parameter that is by reference. If we're InterpOne'ing
240 // it, just assume it might be by reference and smash all the locals.
241 smashesAllLocals = true;
242 break;
243 default:
244 break;
246 break;
248 case OpSetOpL:
249 case OpIncDecL: {
250 assertx(pushedType.hasValue());
251 auto locType = env.irb->localType(localInputId(inst), DataTypeSpecific);
252 assertx(locType < TGen || curFunc(env)->isPseudoMain());
254 auto stackType = pushedType.value();
255 setImmLocType(0, handleBoxiness(locType, stackType));
256 break;
259 case OpStaticLocInit:
260 setImmLocType(0, TBoxedInitCell);
261 break;
263 case OpInitThisLoc:
264 setImmLocType(0, TCell);
265 break;
267 case OpSetL: {
268 auto locType = env.irb->localType(localInputId(inst), DataTypeSpecific);
269 auto stackType = topType(env, BCSPOffset{0});
270 // SetL preserves reffiness of a local.
271 setImmLocType(0, handleBoxiness(locType, stackType));
272 break;
274 case OpVGetL:
275 case OpBindL: {
276 assertx(pushedType.hasValue());
277 assertx(*pushedType <= TBoxedCell);
278 setImmLocType(0, pushedType.value());
279 break;
282 case OpUnsetL:
283 case OpPushL:
284 setImmLocType(0, TUninit);
285 break;
287 // New minstrs are handled extremely conservatively for now.
288 case OpDimL:
289 case OpDimC:
290 case OpDimInt:
291 case OpDimStr:
292 if (inst.imm[2].u_OA & mDefine) smashesAllLocals = true;
293 break;
294 case OpDimNewElem:
295 if (inst.imm[0].u_OA & mDefine) smashesAllLocals = true;
296 break;
297 case OpSetML:
298 case OpSetMC:
299 case OpSetMInt:
300 case OpSetMStr:
301 case OpSetMNewElem:
302 smashesAllLocals = true;
303 break;
305 case OpSetM:
306 case OpSetOpM:
307 case OpBindM:
308 case OpVGetM:
309 case OpSetWithRefLM:
310 case OpSetWithRefRM:
311 case OpUnsetM:
312 case OpIncDecM:
313 switch (inst.immVec.locationCode()) {
314 case LL: {
315 auto const& mii = getMInstrInfo(inst.mInstrOp());
316 auto const& base = inst.inputs[mii.valCount()];
317 assertx(base.space == Location::Local);
319 // MInstrEffects expects to be used in the context of a normally
320 // translated instruction, not an interpOne. The two important
321 // differences are that the base is normally a PtrTo* and we need to
322 // supply an IR opcode representing the operation. SetWithRefElem is
323 // used instead of SetElem because SetElem makes a few assumptions
324 // about side exits that interpOne won't do.
325 auto const baseType = env.irb->localType(
326 base.offset, DataTypeSpecific
327 ).ptr(Ptr::Frame);
328 auto const isUnset = inst.op() == OpUnsetM;
329 auto const isProp = mcodeIsProp(inst.immVecM[0]);
331 if (isUnset && isProp) break;
333 // NullSafe (Q) props don't change the types of locals.
334 if (inst.immVecM[0] == MQT) break;
336 auto op = isProp ? SetProp : isUnset ? UnsetElem : SetWithRefElem;
337 MInstrEffects effects(op, baseType);
338 if (effects.baseValChanged) {
339 auto const ty = effects.baseType.deref();
340 assertx((ty <= TCell ||
341 ty <= TBoxedCell) ||
342 curFunc(env)->isPseudoMain());
343 setLocType(base.offset, handleBoxiness(ty, ty));
345 break;
348 case LNL:
349 case LNC:
350 smashesAllLocals = true;
351 break;
353 default:
354 break;
356 break;
358 case OpMIterInitK:
359 case OpMIterNextK:
360 setImmLocType(3, TCell);
361 /* fallthrough */
362 case OpMIterInit:
363 case OpMIterNext:
364 setImmLocType(2, TBoxedInitCell);
365 break;
367 case OpIterInitK:
368 case OpWIterInitK:
369 case OpIterNextK:
370 case OpWIterNextK:
371 setImmLocType(3, TCell);
372 /* fallthrough */
373 case OpIterInit:
374 case OpWIterInit:
375 case OpIterNext:
376 case OpWIterNext:
377 setImmLocType(2, TGen);
378 break;
380 case OpVerifyParamType: {
381 auto paramId = inst.imm[0].u_LA;
382 auto const& tc = func->params()[paramId].typeConstraint;
383 auto locType = env.irb->localType(localInputId(inst), DataTypeSpecific);
384 if (tc.isArray() && !tc.isSoft() && !func->mustBeRef(paramId) &&
385 (locType <= TObj || locType.maybe(TBoxedCell))) {
386 setImmLocType(0, handleBoxiness(locType, TCell));
388 break;
391 case OpSilence:
392 if (static_cast<SilenceOp>(inst.imm[0].u_OA) == SilenceOp::Start) {
393 setImmLocType(inst.imm[0].u_LA, TInt);
395 break;
397 default:
398 always_assert_flog(
399 false, "Unknown local-modifying op {}", opcodeToName(inst.op())
403 return locals;
406 //////////////////////////////////////////////////////////////////////
410 void interpOne(IRGS& env, const NormalizedInstruction& inst) {
411 folly::Optional<Type> checkTypeType;
412 auto stackType = interpOutputType(env, inst, checkTypeType);
413 auto popped = getStackPopped(inst.pc());
414 auto pushed = getStackPushed(inst.pc());
415 FTRACE(1, "emitting InterpOne for {}, result = {}, popped {}, pushed {}\n",
416 inst.toString(),
417 stackType.hasValue() ? stackType->toString() : "<none>",
418 popped, pushed);
420 InterpOneData idata { offsetFromIRSP(env, BCSPOffset{0}) };
421 auto locals = interpOutputLocals(env, inst, idata.smashesAllLocals,
422 stackType);
423 idata.nChangedLocals = locals.size();
424 idata.changedLocals = locals.data();
426 interpOne(env, stackType, popped, pushed, idata);
427 if (checkTypeType) {
428 auto const out = getInstrInfo(inst.op()).out;
429 auto const checkIdx = BCSPOffset{(out & InstrFlags::StackIns2) ? 2
430 : (out & InstrFlags::StackIns1) ? 1
431 : 0};
432 checkTypeStack(env, checkIdx, *checkTypeType, inst.nextSk().offset(),
433 true /* outerOnly */);
437 void interpOne(IRGS& env, int popped) {
438 InterpOneData idata { offsetFromIRSP(env, BCSPOffset{0}) };
439 interpOne(env, folly::none, popped, 0, idata);
442 void interpOne(IRGS& env, Type outType, int popped) {
443 InterpOneData idata { offsetFromIRSP(env, BCSPOffset{0}) };
444 interpOne(env, outType, popped, 1, idata);
447 void interpOne(IRGS& env,
448 folly::Optional<Type> outType,
449 int popped,
450 int pushed,
451 InterpOneData& idata) {
452 auto const unit = curUnit(env);
453 auto const op = unit->getOp(bcOff(env));
455 idata.bcOff = bcOff(env);
456 idata.cellsPopped = popped;
457 idata.cellsPushed = pushed;
458 idata.opcode = op;
460 gen(
461 env,
462 opcodeChangesPC(idata.opcode) ? InterpOneCF : InterpOne,
463 outType,
464 idata,
465 sp(env),
466 fp(env)
470 //////////////////////////////////////////////////////////////////////
473 * Instructions that unconditionally are implemented with InterpOne are
474 * translated here.
477 #define INTERP interpOne(env, *env.currentNormalizedInstruction);
479 void emitFPushObjMethod(IRGS& env, int32_t, ObjMethodOp) { INTERP }
481 void emitLowInvalid(IRGS& env) { std::abort(); }
482 void emitCGetL3(IRGS& env, int32_t) { INTERP }
483 void emitAddElemV(IRGS& env) { INTERP }
484 void emitAddNewElemV(IRGS& env) { INTERP }
485 void emitClsCns(IRGS& env, const StringData*) { INTERP }
486 void emitExit(IRGS& env) { INTERP }
487 void emitFatal(IRGS& env, FatalOp) { INTERP }
488 void emitUnwind(IRGS& env) { INTERP }
489 void emitThrow(IRGS& env) { INTERP }
490 void emitCGetN(IRGS& env) { INTERP }
491 void emitCGetQuietN(IRGS& env) { INTERP }
492 void emitVGetN(IRGS& env) { INTERP }
493 void emitIssetN(IRGS& env) { INTERP }
494 void emitEmptyN(IRGS& env) { INTERP }
495 void emitSetN(IRGS& env) { INTERP }
496 void emitSetOpN(IRGS& env, SetOpOp) { INTERP }
497 void emitSetOpG(IRGS& env, SetOpOp) { INTERP }
498 void emitSetOpS(IRGS& env, SetOpOp) { INTERP }
499 void emitIncDecN(IRGS& env, IncDecOp) { INTERP }
500 void emitIncDecG(IRGS& env, IncDecOp) { INTERP }
501 void emitIncDecS(IRGS& env, IncDecOp) { INTERP }
502 void emitBindN(IRGS& env) { INTERP }
503 void emitUnsetN(IRGS& env) { INTERP }
504 void emitUnsetG(IRGS& env) { INTERP }
505 void emitFPassN(IRGS& env, int32_t) { INTERP }
506 void emitFCallUnpack(IRGS& env, int32_t) { INTERP }
507 void emitCufSafeArray(IRGS& env) { INTERP }
508 void emitCufSafeReturn(IRGS& env) { INTERP }
509 void emitIncl(IRGS& env) { INTERP }
510 void emitInclOnce(IRGS& env) { INTERP }
511 void emitReq(IRGS& env) { INTERP }
512 void emitReqDoc(IRGS& env) { INTERP }
513 void emitReqOnce(IRGS& env) { INTERP }
514 void emitEval(IRGS& env) { INTERP }
515 void emitDefTypeAlias(IRGS& env, int32_t) { INTERP }
516 void emitDefCns(IRGS& env, const StringData*) { INTERP }
517 void emitDefCls(IRGS& env, int32_t) { INTERP }
518 void emitDefFunc(IRGS& env, int32_t) { INTERP }
519 void emitCatch(IRGS& env) { INTERP }
520 void emitContGetReturn(IRGS& env) { INTERP }
521 void emitHighInvalid(IRGS& env) { std::abort(); }
523 //////////////////////////////////////////////////////////////////////