Add new bytecode instructions for vec/dict/keyset
[hiphop-php.git] / hphp / runtime / vm / jit / irgen-basic.cpp
blob8188b5dd4e4976dccdaa0b193e27dd8ab0e494be
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2016 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/base/stats.h"
18 #include "hphp/runtime/base/strings.h"
20 #include "hphp/runtime/vm/jit/irgen-interpone.h"
21 #include "hphp/runtime/vm/jit/irgen-exit.h"
22 #include "hphp/runtime/vm/jit/irgen-internal.h"
24 namespace HPHP { namespace jit { namespace irgen {
26 namespace {
28 //////////////////////////////////////////////////////////////////////
30 void implAGet(IRGS& env, SSATmp* classSrc) {
31 if (classSrc->type() <= TStr) {
32 push(env, ldCls(env, classSrc));
33 return;
35 push(env, gen(env, LdObjClass, classSrc));
38 const StaticString s_FATAL_NULL_THIS(Strings::FATAL_NULL_THIS);
39 bool checkThis(IRGS& env, SSATmp* ctx) {
40 auto fail = [&] {
41 auto const err = cns(env, s_FATAL_NULL_THIS.get());
42 gen(env, RaiseError, err);
44 if (!ctx->type().maybe(TObj)) {
45 fail();
46 return false;
48 ifThen(
49 env,
50 [&] (Block* taken) {
51 gen(env, CheckCtxThis, taken, ctx);
53 [&] {
54 hint(env, Block::Hint::Unlikely);
55 fail();
58 return true;
61 //////////////////////////////////////////////////////////////////////
65 void emitAGetC(IRGS& env) {
66 auto const name = topC(env);
67 if (name->type().subtypeOfAny(TObj, TStr)) {
68 popC(env);
69 implAGet(env, name);
70 decRef(env, name);
71 } else {
72 interpOne(env, TCls, 1);
76 void emitAGetL(IRGS& env, int32_t id) {
77 auto const ldrefExit = makeExit(env);
78 auto const ldPMExit = makePseudoMainExit(env);
79 auto const src = ldLocInner(env, id, ldrefExit, ldPMExit, DataTypeSpecific);
80 if (src->type().subtypeOfAny(TObj, TStr)) {
81 implAGet(env, src);
82 } else {
83 PUNT(AGetL);
87 void emitCGetL(IRGS& env, int32_t id) {
88 auto const ldrefExit = makeExit(env);
89 auto const ldPMExit = makePseudoMainExit(env);
90 auto const loc = ldLocInnerWarn(
91 env,
92 id,
93 ldrefExit,
94 ldPMExit,
95 DataTypeCountnessInit
97 pushIncRef(env, loc);
100 void emitCGetQuietL(IRGS& env, int32_t id) {
101 auto const ldrefExit = makeExit(env);
102 auto const ldPMExit = makePseudoMainExit(env);
103 auto const loc = ldLocInner(
104 env,
106 ldrefExit,
107 ldPMExit,
108 DataTypeCountnessInit
110 pushIncRef(env, loc);
113 void emitCUGetL(IRGS& env, int32_t id) {
114 auto const ldrefExit = makeExit(env);
115 auto const ldPMExit = makePseudoMainExit(env);
116 pushIncRef(env, ldLocInner(env, id, ldrefExit, ldPMExit, DataTypeGeneric));
119 void emitPushL(IRGS& env, int32_t id) {
120 assertTypeLocal(env, id, TInitCell); // bytecode invariant
121 auto* locVal = ldLoc(env, id, makeExit(env), DataTypeGeneric);
122 push(env, locVal);
123 stLocRaw(env, id, fp(env), cns(env, TUninit));
126 void emitCGetL2(IRGS& env, int32_t id) {
127 auto const ldrefExit = makeExit(env);
128 auto const ldPMExit = makePseudoMainExit(env);
129 auto const oldTop = pop(env, DataTypeGeneric);
130 auto const val = ldLocInnerWarn(
131 env,
133 ldrefExit,
134 ldPMExit,
135 DataTypeCountnessInit
137 pushIncRef(env, val);
138 push(env, oldTop);
141 template<class F>
142 SSATmp* boxHelper(IRGS& env, SSATmp* value, F rewrite) {
143 auto const t = value->type();
144 if (t <= TCell) {
145 if (t <= TUninit) {
146 value = cns(env, TInitNull);
148 value = gen(env, Box, value);
149 rewrite(value);
150 } else if (t.maybe(TCell)) {
151 value = cond(env,
152 [&](Block* taken) {
153 auto const ret = gen(env, CheckType, TBoxedInitCell,
154 taken, value);
155 env.irb->constrainValue(ret, DataTypeSpecific);
156 return ret;
158 [&](SSATmp* box) { // Next: value is Boxed
159 return box;
161 [&] { // Taken: value is not Boxed
162 auto const tmpType = t - TBoxedInitCell;
163 assertx(tmpType <= TCell);
164 auto const tmp = gen(env, AssertType, tmpType, value);
165 auto const ret = gen(env, Box, tmp);
166 rewrite(ret);
167 return ret;
170 return value;
173 void emitVGetL(IRGS& env, int32_t id) {
174 auto const value = ldLoc(env, id, makeExit(env), DataTypeCountnessInit);
175 auto const boxed = boxHelper(
176 env,
177 gen(env, AssertType, TCell | TBoxedInitCell, value),
178 [&] (SSATmp* v) {
179 stLocRaw(env, id, fp(env), v);
182 pushIncRef(env, boxed);
185 void emitBox(IRGS& env) {
186 push(env, gen(env, Box, pop(env, DataTypeGeneric)));
189 void emitBoxR(IRGS& env) {
190 auto const value = pop(env, DataTypeGeneric);
191 auto const boxed = boxHelper(
192 env,
193 gen(env, AssertType, TCell | TBoxedInitCell, value),
194 [] (SSATmp* ) {});
195 push(env, boxed);
198 void emitUnsetL(IRGS& env, int32_t id) {
199 auto const prev = ldLoc(env, id, makeExit(env), DataTypeCountness);
200 stLocRaw(env, id, fp(env), cns(env, TUninit));
201 decRef(env, prev);
204 void emitBindL(IRGS& env, int32_t id) {
205 if (curFunc(env)->isPseudoMain()) {
206 interpOne(env, TBoxedInitCell, 1);
207 return;
210 auto const ldPMExit = makePseudoMainExit(env);
211 auto const newValue = popV(env);
212 // Note that the IncRef must happen first, for correctness in a
213 // pseudo-main: the destructor could decref the value again after
214 // we've stored it into the local.
215 pushIncRef(env, newValue);
216 auto const oldValue = ldLoc(env, id, ldPMExit, DataTypeSpecific);
217 stLocRaw(env, id, fp(env), newValue);
218 decRef(env, oldValue);
221 void emitSetL(IRGS& env, int32_t id) {
222 auto const ldrefExit = makeExit(env);
223 auto const ldPMExit = makePseudoMainExit(env);
225 // since we're just storing the value in a local, this function doesn't care
226 // about the type of the value. stLoc needs to IncRef the value so it may
227 // constrain it further.
228 auto const src = popC(env, DataTypeGeneric);
229 pushStLoc(env, id, ldrefExit, ldPMExit, src);
232 void emitInitThisLoc(IRGS& env, int32_t id) {
233 if (!curFunc(env)->mayHaveThis()) {
234 // Do nothing if this is null
235 return;
237 auto const ldrefExit = makeExit(env);
238 auto const ctx = ldCtx(env);
239 ifElse(env,
240 [&] (Block* skip) { gen(env, CheckCtxThis, skip, ctx); },
241 [&] {
242 auto const oldLoc = ldLoc(env, id, ldrefExit, DataTypeCountness);
243 auto const this_ = gen(env, CastCtxThis, ctx);
244 gen(env, IncRef, this_);
245 stLocRaw(env, id, fp(env), this_);
246 decRef(env, oldLoc);
250 void emitPrint(IRGS& env) {
251 auto const type = topC(env)->type();
252 if (!type.subtypeOfAny(TInt, TBool, TNull, TStr)) {
253 interpOne(env, TInt, 1);
254 return;
257 auto const cell = popC(env);
259 Opcode op;
260 if (type <= TStr) {
261 op = PrintStr;
262 } else if (type <= TInt) {
263 op = PrintInt;
264 } else if (type <= TBool) {
265 op = PrintBool;
266 } else {
267 assertx(type <= TNull);
268 op = Nop;
270 // the print helpers decref their arg, so don't decref pop'ed value
271 if (op != Nop) {
272 gen(env, op, cell);
274 push(env, cns(env, 1));
277 void emitUnbox(IRGS& env) {
278 auto const exit = makeExit(env);
279 auto const srcBox = popV(env);
280 auto const unboxed = unbox(env, srcBox, exit);
281 pushIncRef(env, unboxed);
282 decRef(env, srcBox);
285 void emitThis(IRGS& env) {
286 auto const ctx = ldCtx(env);
287 if (!checkThis(env, ctx)) {
288 // Unreachable
289 push(env, cns(env, TInitNull));
290 return;
292 auto const this_ = gen(env, CastCtxThis, ctx);
293 pushIncRef(env, this_);
296 void emitCheckThis(IRGS& env) {
297 auto const ctx = ldCtx(env);
298 checkThis(env, ctx);
301 void emitBareThis(IRGS& env, BareThisOp subop) {
302 auto const ctx = ldCtx(env);
303 if (!ctx->type().maybe(TObj)) {
304 if (subop == BareThisOp::NoNotice) {
305 push(env, cns(env, TInitNull));
306 return;
308 assertx(subop != BareThisOp::NeverNull);
309 interpOne(env, TInitNull, 0); // will raise notice and push null
310 return;
313 if (subop == BareThisOp::NoNotice) {
314 auto thiz = cond(env,
315 [&](Block* taken) {
316 gen(env, CheckCtxThis, taken, ctx);
318 [&] {
319 auto t = gen(env, CastCtxThis, ctx);
320 gen(env, IncRef, t);
321 return t;
323 [&] {
324 return cns(env, TInitNull);
326 push(env, thiz);
327 return;
330 if (subop == BareThisOp::NeverNull) {
331 env.irb->fs().setThisAvailable();
332 } else {
333 gen(env, CheckCtxThis, makeExitSlow(env), ctx);
336 pushIncRef(env, gen(env, CastCtxThis, ctx));
339 void emitClone(IRGS& env) {
340 if (!topC(env)->isA(TObj)) PUNT(Clone-NonObj);
341 auto const obj = popC(env);
342 push(env, gen(env, Clone, obj));
343 decRef(env, obj);
346 void emitLateBoundCls(IRGS& env) {
347 auto const clss = curClass(env);
348 if (!clss) {
349 // no static context class, so this will raise an error
350 interpOne(env, TCls, 0);
351 return;
353 auto const ctx = ldCtx(env);
354 push(env, gen(env, LdClsCtx, ctx));
357 void emitSelf(IRGS& env) {
358 auto const clss = curClass(env);
359 if (clss == nullptr) {
360 interpOne(env, TCls, 0);
361 } else {
362 push(env, cns(env, clss));
366 void emitParent(IRGS& env) {
367 auto const clss = curClass(env);
368 if (clss == nullptr || clss->parent() == nullptr) {
369 interpOne(env, TCls, 0);
370 } else {
371 push(env, cns(env, clss->parent()));
375 void emitNameA(IRGS& env) {
376 push(env, gen(env, LdClsName, popA(env)));
379 //////////////////////////////////////////////////////////////////////
381 void emitCastArray(IRGS& env) {
382 auto const src = popC(env);
383 push(
384 env,
385 [&] {
386 if (src->isA(TArr)) {
387 return cond(
388 env,
389 [&](Block* taken) {
390 return gen(
391 env,
392 CheckType,
393 Type::Array(ArrayData::kVecKind),
394 taken,
398 [&](SSATmp* vec) { return gen(env, ConvVecToArr, vec); },
399 [&] {
400 return cond(
401 env,
402 [&](Block* taken) {
403 return gen(
404 env,
405 CheckType,
406 Type::Array(ArrayData::kDictKind),
407 taken,
411 [&](SSATmp* dict) { return gen(env, ConvDictToArr, dict); },
412 [&] { return src; }
417 if (src->isA(TNull)) return cns(env, staticEmptyArray());
418 if (src->isA(TBool)) return gen(env, ConvBoolToArr, src);
419 if (src->isA(TDbl)) return gen(env, ConvDblToArr, src);
420 if (src->isA(TInt)) return gen(env, ConvIntToArr, src);
421 if (src->isA(TStr)) return gen(env, ConvStrToArr, src);
422 if (src->isA(TObj)) return gen(env, ConvObjToArr, src);
423 return gen(env, ConvCellToArr, src);
428 void emitCastBool(IRGS& env) {
429 auto const src = popC(env);
430 push(env, gen(env, ConvCellToBool, src));
431 decRef(env, src);
434 void emitCastDouble(IRGS& env) {
435 auto const src = popC(env);
436 push(env, gen(env, ConvCellToDbl, src));
437 decRef(env, src);
440 void emitCastInt(IRGS& env) {
441 auto const src = popC(env);
442 push(env, gen(env, ConvCellToInt, src));
443 decRef(env, src);
446 void emitCastObject(IRGS& env) {
447 auto const src = popC(env);
448 push(env, gen(env, ConvCellToObj, src));
451 void emitCastString(IRGS& env) {
452 auto const src = popC(env);
453 push(env, gen(env, ConvCellToStr, src));
454 decRef(env, src);
457 void emitIncStat(IRGS& env, int32_t counter, int32_t value) {
458 if (!Stats::enabled()) return;
459 gen(env, IncStat, cns(env, counter), cns(env, value), cns(env, false));
462 //////////////////////////////////////////////////////////////////////
464 void emitPopA(IRGS& env) { popA(env); }
465 void emitPopC(IRGS& env) { popDecRef(env, DataTypeGeneric); }
466 void emitPopV(IRGS& env) { popDecRef(env, DataTypeGeneric); }
467 void emitPopR(IRGS& env) { popDecRef(env, DataTypeGeneric); }
469 void emitDir(IRGS& env) { push(env, cns(env, curUnit(env)->dirpath())); }
470 void emitFile(IRGS& env) { push(env, cns(env, curUnit(env)->filepath())); }
472 void emitDup(IRGS& env) { pushIncRef(env, topC(env)); }
474 //////////////////////////////////////////////////////////////////////
476 void emitArray(IRGS& env, const ArrayData* x) {
477 assertx(x->isPHPArray());
478 push(env, cns(env, x));
481 void emitVec(IRGS& env, const ArrayData* x) {
482 assertx(x->isVecArray());
483 push(env, cns(env, x));
486 void emitDict(IRGS& env, const ArrayData* x) {
487 assertx(x->isDict());
488 push(env, cns(env, x));
491 void emitKeyset(IRGS& env, const ArrayData* x) {
492 assertx(x->isKeyset());
493 push(env, cns(env, x));
496 void emitString(IRGS& env, const StringData* s) { push(env, cns(env, s)); }
497 void emitInt(IRGS& env, int64_t val) { push(env, cns(env, val)); }
498 void emitDouble(IRGS& env, double val) { push(env, cns(env, val)); }
499 void emitTrue(IRGS& env) { push(env, cns(env, true)); }
500 void emitFalse(IRGS& env) { push(env, cns(env, false)); }
502 void emitNull(IRGS& env) { push(env, cns(env, TInitNull)); }
503 void emitNullUninit(IRGS& env) { push(env, cns(env, TUninit)); }
505 //////////////////////////////////////////////////////////////////////
507 void emitNop(IRGS&) {}
508 void emitEntryNop(IRGS&) {}
509 void emitBoxRNop(IRGS& env) {
510 assertTypeStack(env, BCSPRelOffset{0}, TBoxedCell);
512 void emitUnboxRNop(IRGS& env) {
513 assertTypeStack(env, BCSPRelOffset{0}, TCell);
515 void emitRGetCNop(IRGS&) {}
516 void emitFPassC(IRGS&, int32_t) {}
517 void emitFPassVNop(IRGS&, int32_t) {}
518 void emitDefClsNop(IRGS&, Id) {}
519 void emitBreakTraceHint(IRGS&) {}
521 //////////////////////////////////////////////////////////////////////