forked from facebook/hhvm
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathirgen-interpone.cpp
486 lines (421 loc) · 16.2 KB
/
irgen-interpone.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010-2014 Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| [email protected] so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include "hphp/runtime/vm/jit/irgen-interpone.h"
#include <cstdlib>
#include "hphp/runtime/vm/jit/minstr-effects.h"
#include "hphp/runtime/vm/jit/normalized-instruction.h"
#include "hphp/runtime/vm/jit/irgen-exit.h"
#include "hphp/runtime/vm/jit/irgen-guards.h"
#include "hphp/runtime/vm/jit/irgen-internal.h"
namespace HPHP { namespace jit { namespace irgen {
namespace {
//////////////////////////////////////////////////////////////////////
Type arithOpResult(Type t1, Type t2) {
if (!t1.isKnownDataType() || !t2.isKnownDataType()) {
return Type::Cell;
}
auto both = t1 | t2;
if (both.maybe(Type::Dbl)) return Type::Dbl;
if (both.maybe(Type::Arr)) return Type::Arr;
if (both.maybe(Type::Str)) return Type::Cell;
return Type::Int;
}
Type arithOpOverResult(Type t1, Type t2) {
if (t1 <= Type::Int && t2 <= Type::Int) {
return Type::Int | Type::Dbl;
}
return arithOpResult(t1, t2);
}
Type bitOpResult(Type t1, Type t2) {
if (!t1.isKnownDataType() || !t2.isKnownDataType()) {
return Type::Cell;
}
auto both = t1 | t2;
if (both <= Type::Str) return Type::Str;
return Type::Int;
}
Type setOpResult(Type locType, Type valType, SetOpOp op) {
switch (op) {
case SetOpOp::PlusEqual:
case SetOpOp::MinusEqual:
case SetOpOp::MulEqual: return arithOpResult(locType.unbox(), valType);
case SetOpOp::PlusEqualO:
case SetOpOp::MinusEqualO:
case SetOpOp::MulEqualO: return arithOpOverResult(locType.unbox(), valType);
case SetOpOp::ConcatEqual: return Type::Str;
case SetOpOp::PowEqual:
case SetOpOp::DivEqual:
case SetOpOp::ModEqual: return Type::UncountedInit;
case SetOpOp::AndEqual:
case SetOpOp::OrEqual:
case SetOpOp::XorEqual: return bitOpResult(locType.unbox(), valType);
case SetOpOp::SlEqual:
case SetOpOp::SrEqual: return Type::Int;
}
not_reached();
}
uint32_t localInputId(const NormalizedInstruction& inst) {
switch (inst.op()) {
case OpSetWithRefLM:
case OpFPassL:
return inst.imm[1].u_LA;
default:
return inst.imm[0].u_LA;
}
}
folly::Optional<Type> interpOutputType(HTS& env,
const NormalizedInstruction& inst,
folly::Optional<Type>& checkTypeType) {
using namespace jit::InstrFlags;
auto localType = [&]{
auto locId = localInputId(inst);
static_assert(std::is_unsigned<typeof(locId)>::value,
"locId should be unsigned");
assert(locId < curFunc(env)->numLocals());
return env.irb->localType(locId, DataTypeSpecific);
};
auto boxed = [&] (Type t) -> Type {
if (t.equals(Type::Gen)) return Type::BoxedInitCell;
assert(t.isBoxed() || t.notBoxed());
checkTypeType = t.isBoxed() ? t : boxType(t); // inner type is predicted
return Type::BoxedInitCell;
};
auto outFlag = getInstrInfo(inst.op()).type;
if (outFlag == OutFInputL) {
outFlag = inst.preppedByRef ? OutVInputL : OutCInputL;
} else if (outFlag == OutFInputR) {
outFlag = inst.preppedByRef ? OutVInput : OutCInput;
}
switch (outFlag) {
case OutNull: return Type::InitNull;
case OutNullUninit: return Type::Uninit;
case OutString: return Type::Str;
case OutStringImm: return Type::StaticStr;
case OutDouble: return Type::Dbl;
case OutIsTypeL:
case OutBoolean:
case OutPredBool:
case OutBooleanImm: return Type::Bool;
case OutInt64: return Type::Int;
case OutArray: return Type::Arr;
case OutArrayImm: return Type::Arr; // Should be StaticArr: t2124292
case OutObject:
case OutThisObject: return Type::Obj;
case OutResource: return Type::Res;
case OutFDesc: return folly::none;
case OutUnknown: return Type::Gen;
case OutCns: return Type::Cell;
case OutVUnknown: return Type::BoxedInitCell;
case OutSameAsInput: return topType(env, 0);
case OutVInput: return boxed(topType(env, 0));
case OutVInputL: return boxed(localType());
case OutFInputL:
case OutFInputR: not_reached();
case OutArith: return arithOpResult(topType(env, 0),
topType(env, 1));
case OutArithO: return arithOpOverResult(topType(env, 0),
topType(env, 1));
case OutBitOp:
return bitOpResult(topType(env, 0),
inst.op() == HPHP::OpBitNot ? Type::Bottom
: topType(env, 1));
case OutSetOp: return setOpResult(localType(), topType(env, 0),
SetOpOp(inst.imm[1].u_OA));
case OutIncDec: {
auto ty = localType().unbox();
return ty <= Type::Dbl ? ty : Type::Cell;
}
case OutStrlen:
return topType(env, 0) <= Type::Str ? Type::Int : Type::UncountedInit;
case OutClassRef: return Type::Cls;
case OutFPushCufSafe: return folly::none;
case OutNone: return folly::none;
case OutCInput: {
auto ttype = topType(env, 0);
if (ttype.notBoxed()) return ttype;
// All instructions that are OutCInput or OutCInputL cannot push uninit or
// a ref, so only specific inner types need to be checked.
if (ttype.unbox().strictSubtypeOf(Type::InitCell)) {
checkTypeType = ttype.unbox();
}
return Type::Cell;
}
case OutCInputL: {
auto ltype = localType();
if (ltype.notBoxed()) return ltype;
if (ltype.unbox().strictSubtypeOf(Type::InitCell)) {
checkTypeType = ltype.unbox();
}
return Type::Cell;
}
}
not_reached();
}
jit::vector<InterpOneData::LocalType>
interpOutputLocals(HTS& env,
const NormalizedInstruction& inst,
bool& smashesAllLocals,
folly::Optional<Type> pushedType) {
using namespace jit::InstrFlags;
if (!(getInstrInfo(inst.op()).out & Local)) return {};
jit::vector<InterpOneData::LocalType> locals;
auto setLocType = [&](uint32_t id, Type t) {
// Relax the type to something guardable. For InterpOne we don't bother to
// keep track of specialized types or inner-ref types. (And note that for
// psuedomains we may in fact have to guard on the local type after this.)
locals.emplace_back(id, t.relaxToGuardable());
};
auto setImmLocType = [&](uint32_t id, Type t) {
setLocType(inst.imm[id].u_LA, t);
};
auto const func = curFunc(env);
auto handleBoxiness = [&] (Type testTy, Type useTy) {
return testTy.isBoxed() ? Type::BoxedInitCell :
testTy.maybeBoxed() ? Type::Gen :
useTy;
};
switch (inst.op()) {
case OpSetN:
case OpSetOpN:
case OpIncDecN:
case OpBindN:
case OpVGetN:
case OpUnsetN:
smashesAllLocals = true;
break;
case OpSetOpL:
case OpIncDecL: {
assert(pushedType.hasValue());
auto locType = env.irb->localType(localInputId(inst), DataTypeSpecific);
assert(locType < Type::Gen || curFunc(env)->isPseudoMain());
auto stackType = pushedType.value();
setImmLocType(0, handleBoxiness(locType, stackType));
break;
}
case OpStaticLocInit:
setImmLocType(0, Type::BoxedInitCell);
break;
case OpInitThisLoc:
setImmLocType(0, Type::Cell);
break;
case OpSetL: {
auto locType = env.irb->localType(localInputId(inst), DataTypeSpecific);
auto stackType = topType(env, 0);
// SetL preserves reffiness of a local.
setImmLocType(0, handleBoxiness(locType, stackType));
break;
}
case OpVGetL:
case OpBindL: {
assert(pushedType.hasValue());
assert(pushedType->isBoxed());
setImmLocType(0, pushedType.value());
break;
}
case OpUnsetL:
case OpPushL:
setImmLocType(0, Type::Uninit);
break;
case OpSetM:
case OpSetOpM:
case OpBindM:
case OpVGetM:
case OpSetWithRefLM:
case OpSetWithRefRM:
case OpUnsetM:
case OpFPassM:
case OpIncDecM:
switch (inst.immVec.locationCode()) {
case LL: {
auto const& mii = getMInstrInfo(inst.mInstrOp());
auto const& base = inst.inputs[mii.valCount()]->location;
assert(base.space == Location::Local);
// MInstrEffects expects to be used in the context of a normally
// translated instruction, not an interpOne. The two important
// differences are that the base is normally a PtrTo* and we need to
// supply an IR opcode representing the operation. SetWithRefElem is
// used instead of SetElem because SetElem makes a few assumptions
// about side exits that interpOne won't do.
auto const baseType = env.irb->localType(
base.offset, DataTypeSpecific
).ptr(Ptr::Frame);
auto const isUnset = inst.op() == OpUnsetM;
auto const isProp = mcodeIsProp(inst.immVecM[0]);
if (isUnset && isProp) break;
auto op = isProp ? SetProp : isUnset ? UnsetElem : SetWithRefElem;
MInstrEffects effects(op, baseType);
if (effects.baseValChanged) {
auto const ty = effects.baseType.deref();
assert((ty.isBoxed() ||
ty.notBoxed()) ||
curFunc(env)->isPseudoMain());
setLocType(base.offset, handleBoxiness(ty, ty));
}
break;
}
case LNL:
case LNC:
smashesAllLocals = true;
break;
default:
break;
}
break;
case OpMIterInitK:
case OpMIterNextK:
setImmLocType(3, Type::Cell);
case OpMIterInit:
case OpMIterNext:
setImmLocType(2, Type::BoxedInitCell);
break;
case OpIterInitK:
case OpWIterInitK:
case OpIterNextK:
case OpWIterNextK:
setImmLocType(3, Type::Cell);
case OpIterInit:
case OpWIterInit:
case OpIterNext:
case OpWIterNext:
setImmLocType(2, Type::Gen);
break;
case OpVerifyParamType: {
auto paramId = inst.imm[0].u_LA;
auto const& tc = func->params()[paramId].typeConstraint;
auto locType = env.irb->localType(localInputId(inst), DataTypeSpecific);
if (tc.isArray() && !tc.isSoft() && !func->mustBeRef(paramId) &&
(locType <= Type::Obj || locType.maybeBoxed())) {
setImmLocType(0, handleBoxiness(locType, Type::Cell));
}
break;
}
case OpSilence:
if (static_cast<SilenceOp>(inst.imm[0].u_OA) == SilenceOp::Start) {
setImmLocType(inst.imm[0].u_LA, Type::Int);
}
break;
default:
not_reached();
}
return locals;
}
//////////////////////////////////////////////////////////////////////
}
void interpOne(HTS& env, const NormalizedInstruction& inst) {
folly::Optional<Type> checkTypeType;
auto stackType = interpOutputType(env, inst, checkTypeType);
auto popped = getStackPopped(inst.pc());
auto pushed = getStackPushed(inst.pc());
FTRACE(1, "emitting InterpOne for {}, result = {}, popped {}, pushed {}\n",
inst.toString(),
stackType.hasValue() ? stackType->toString() : "<none>",
popped, pushed);
InterpOneData idata;
auto locals = interpOutputLocals(env, inst, idata.smashesAllLocals,
stackType);
idata.nChangedLocals = locals.size();
idata.changedLocals = locals.data();
interpOne(env, stackType, popped, pushed, idata);
if (checkTypeType) {
auto const out = getInstrInfo(inst.op()).out;
auto const checkIdx = (out & InstrFlags::StackIns2) ? 2
: (out & InstrFlags::StackIns1) ? 1
: 0;
checkTypeStack(env, checkIdx, *checkTypeType, inst.nextSk().offset());
}
}
void interpOne(HTS& env, int popped) {
InterpOneData idata;
interpOne(env, folly::none, popped, 0, idata);
}
void interpOne(HTS& env, Type outType, int popped) {
InterpOneData idata;
interpOne(env, outType, popped, 1, idata);
}
void interpOne(HTS& env,
folly::Optional<Type> outType,
int popped,
int pushed,
InterpOneData& idata) {
auto const unit = curUnit(env);
auto const stack = spillStack(env);
env.irb->exceptionStackBoundary();
auto const op = unit->getOpcode(bcOff(env));
auto& iInfo = getInstrInfo(op);
if (iInfo.type == jit::InstrFlags::OutFDesc) {
env.fpiStack.emplace(stack, env.irb->spOffset());
} else if (isFCallStar(op) && !env.fpiStack.empty()) {
env.fpiStack.pop();
}
idata.bcOff = bcOff(env);
idata.cellsPopped = popped;
idata.cellsPushed = pushed;
idata.opcode = op;
auto const changesPC = opcodeChangesPC(idata.opcode);
gen(env, changesPC ? InterpOneCF : InterpOne, outType,
idata, stack, fp(env));
assert(env.irb->stackDeficit() == 0);
}
//////////////////////////////////////////////////////////////////////
/*
* Instructions that unconditionally are implemented with InterpOne are
* translated here.
*/
#define INTERP interpOne(env, *env.currentNormalizedInstruction);
void emitFPushObjMethod(HTS& env, int32_t, ObjMethodOp) { INTERP }
void emitLowInvalid(HTS& env) { std::abort(); }
void emitCGetL3(HTS& env, int32_t) { INTERP }
void emitBox(HTS& env) { INTERP }
void emitBoxR(HTS& env) { INTERP }
void emitAddElemV(HTS& env) { INTERP }
void emitAddNewElemV(HTS& env) { INTERP }
void emitClsCns(HTS& env, const StringData*) { INTERP }
void emitExit(HTS& env) { INTERP }
void emitFatal(HTS& env, FatalOp) { INTERP }
void emitUnwind(HTS& env) { INTERP }
void emitThrow(HTS& env) { INTERP }
void emitCGetN(HTS& env) { INTERP }
void emitVGetN(HTS& env) { INTERP }
void emitIssetN(HTS& env) { INTERP }
void emitEmptyN(HTS& env) { INTERP }
void emitSetN(HTS& env) { INTERP }
void emitSetOpN(HTS& env, SetOpOp) { INTERP }
void emitSetOpG(HTS& env, SetOpOp) { INTERP }
void emitSetOpS(HTS& env, SetOpOp) { INTERP }
void emitIncDecN(HTS& env, IncDecOp) { INTERP }
void emitIncDecG(HTS& env, IncDecOp) { INTERP }
void emitIncDecS(HTS& env, IncDecOp) { INTERP }
void emitBindN(HTS& env) { INTERP }
void emitUnsetN(HTS& env) { INTERP }
void emitUnsetG(HTS& env) { INTERP }
void emitFPassN(HTS& env, int32_t) { INTERP }
void emitFCallUnpack(HTS& env, int32_t) { INTERP }
void emitCufSafeArray(HTS& env) { INTERP }
void emitCufSafeReturn(HTS& env) { INTERP }
void emitIncl(HTS& env) { INTERP }
void emitInclOnce(HTS& env) { INTERP }
void emitReq(HTS& env) { INTERP }
void emitReqDoc(HTS& env) { INTERP }
void emitReqOnce(HTS& env) { INTERP }
void emitEval(HTS& env) { INTERP }
void emitDefTypeAlias(HTS& env, int32_t) { INTERP }
void emitDefCns(HTS& env, const StringData*) { INTERP }
void emitDefCls(HTS& env, int32_t) { INTERP }
void emitDefFunc(HTS& env, int32_t) { INTERP }
void emitCatch(HTS& env) { INTERP }
void emitHighInvalid(HTS& env) { std::abort(); }
//////////////////////////////////////////////////////////////////////
}}}