Added IterBreakV, MIter{Init,InitK,Next,NextK,Free} and fixed memory tracking bug.
[hiphop-php.git] / hphp / runtime / vm / hhbc.cpp
blobcfd20e2681c28536d73e1396e49797482bdc7db9
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2013 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/hhbc.h"
18 #include "hphp/runtime/ext/ext_variable.h"
19 #include "hphp/runtime/vm/unit.h"
20 #include "hphp/runtime/base/stats.h"
21 #include <sstream>
23 namespace HPHP {
24 ///////////////////////////////////////////////////////////////////////////////
26 int numImmediates(Op opcode) {
27 assert(isValidOpcode(opcode));
28 static const int8_t values[] = {
29 #define NA 0
30 #define ONE(...) 1
31 #define TWO(...) 2
32 #define THREE(...) 3
33 #define FOUR(...) 4
34 #define O(name, imm, unusedPop, unusedPush, unusedFlags) imm,
35 OPCODES
36 #undef O
37 #undef NA
38 #undef ONE
39 #undef TWO
40 #undef THREE
41 #undef FOUR
43 return values[uint8_t(opcode)];
46 ArgType immType(const Op opcode, int idx) {
47 assert(isValidOpcode(opcode));
48 assert(idx >= 0 && idx < numImmediates(opcode));
49 always_assert(idx < 4); // No opcodes have more than four immediates
50 static const int8_t arg0Types[] = {
51 #define NA -1,
52 #define ONE(a) a,
53 #define TWO(a, b) a,
54 #define THREE(a, b, c) a,
55 #define FOUR(a, b, c, d) a,
56 #define O(name, imm, unusedPop, unusedPush, unusedFlags) imm
57 OPCODES
58 // re-using definition of O below.
59 #undef NA
60 #undef ONE
61 #undef TWO
62 #undef THREE
63 #undef FOUR
65 static const int8_t arg1Types[] = {
66 #define NA -1,
67 #define ONE(a) -1,
68 #define TWO(a, b) b,
69 #define THREE(a, b, c) b,
70 #define FOUR(a, b, c, d) b,
71 OPCODES
72 // re-using definition of O below.
73 #undef NA
74 #undef ONE
75 #undef TWO
76 #undef THREE
77 #undef FOUR
79 static const int8_t arg2Types[] = {
80 #define NA -1,
81 #define ONE(a) -1,
82 #define TWO(a, b) -1,
83 #define THREE(a, b, c) c,
84 #define FOUR(a, b, c, d) c,
85 OPCODES
86 #undef NA
87 #undef ONE
88 #undef TWO
89 #undef THREE
90 #undef FOUR
92 static const int8_t arg3Types[] = {
93 #define NA -1,
94 #define ONE(a) -1,
95 #define TWO(a, b) -1,
96 #define THREE(a, b, c) -1,
97 #define FOUR(a, b, c, d) d,
98 OPCODES
99 #undef O
100 #undef NA
101 #undef ONE
102 #undef TWO
103 #undef THREE
104 #undef FOUR
106 auto opInt = uint8_t(opcode);
107 switch (idx) {
108 case 0: return (ArgType)arg0Types[opInt];
109 case 1: return (ArgType)arg1Types[opInt];
110 case 2: return (ArgType)arg2Types[opInt];
111 case 3: return (ArgType)arg3Types[opInt];
112 default: assert(false); return (ArgType)-1;
116 int immSize(const Op* opcode, int idx) {
117 assert(idx >= 0 && idx < numImmediates(*opcode));
118 always_assert(idx < 4); // No opcodes have more than four immediates
119 static const int8_t argTypeToSizes[] = {
120 #define ARGTYPE(nm, type) sizeof(type),
121 #define ARGTYPEVEC(nm, type) 0,
122 ARGTYPES
123 #undef ARGTYPE
124 #undef ARGTYPEVEC
126 if (immType(*opcode, idx) == IVA || immType(*opcode, idx) == HA ||
127 immType(*opcode, idx) == IA) {
128 intptr_t offset = 1;
129 if (idx >= 1) offset += immSize(opcode, 0);
130 if (idx >= 2) offset += immSize(opcode, 1);
131 if (idx >= 3) offset += immSize(opcode, 2);
132 // variable size
133 unsigned char imm = *(unsigned char*)(opcode + offset);
134 // Low order bit set => 4-byte.
135 return (imm & 0x1 ? sizeof(int32_t) : sizeof(unsigned char));
136 } else if (immIsVector(*opcode, idx)) {
137 intptr_t offset = 1;
138 if (idx >= 1) offset += immSize(opcode, 0);
139 if (idx >= 2) offset += immSize(opcode, 1);
140 if (idx >= 3) offset += immSize(opcode, 2);
141 int prefixes, vecElemSz;
142 auto itype = immType(*opcode, idx);
143 if (itype == MA) {
144 prefixes = 2;
145 vecElemSz = sizeof(uint8_t);
146 } else if (itype == BLA) {
147 prefixes = 1;
148 vecElemSz = sizeof(Offset);
149 } else if (itype == ILA) {
150 prefixes = 1;
151 vecElemSz = 2 * sizeof(uint32_t);
152 } else {
153 assert(itype == SLA);
154 prefixes = 1;
155 vecElemSz = sizeof(StrVecItem);
157 return prefixes * sizeof(int32_t) +
158 vecElemSz * *(int32_t*)((int8_t*)opcode + offset);
159 } else {
160 ArgType type = immType(*opcode, idx);
161 return (type >= 0) ? argTypeToSizes[type] : 0;
165 bool immIsVector(Op opcode, int idx) {
166 ArgType type = immType(opcode, idx);
167 return (type == MA || type == BLA || type == SLA || type == ILA);
170 bool hasImmVector(Op opcode) {
171 const int num = numImmediates(opcode);
172 for (int i = 0; i < num; ++i) {
173 if (immIsVector(opcode, i)) return true;
175 return false;
178 ArgUnion getImm(const Op* opcode, int idx) {
179 const Op* p = opcode + 1;
180 assert(idx >= 0 && idx < numImmediates(*opcode));
181 ArgUnion retval;
182 retval.u_NA = 0;
183 int cursor = 0;
184 for (cursor = 0; cursor < idx; cursor++) {
185 // Advance over this immediate.
186 p += immSize(opcode, cursor);
188 always_assert(cursor == idx);
189 ArgType type = immType(*opcode, idx);
190 if (type == IVA || type == HA || type == IA) {
191 retval.u_IVA = decodeVariableSizeImm((const uint8_t**)&p);
192 } else if (!immIsVector(*opcode, cursor)) {
193 memcpy(&retval.bytes, p, immSize(opcode, idx));
195 always_assert(numImmediates(*opcode) > idx);
196 return retval;
199 ArgUnion* getImmPtr(const Op* opcode, int idx) {
200 assert(immType(*opcode, idx) != IVA);
201 assert(immType(*opcode, idx) != HA);
202 assert(immType(*opcode, idx) != IA);
203 const Op* ptr = opcode + 1;
204 for (int i = 0; i < idx; i++) {
205 ptr += immSize(opcode, i);
207 return (ArgUnion*)ptr;
210 template<typename T>
211 T decodeImm(const unsigned char** immPtr) {
212 T val = *(T*)*immPtr;
213 *immPtr += sizeof(T);
214 return val;
217 int64_t decodeMemberCodeImm(const unsigned char** immPtr, MemberCode mcode) {
218 switch (mcode) {
219 case MEL:
220 case MPL:
221 return decodeVariableSizeImm(immPtr);
223 case MET:
224 case MPT:
225 return decodeImm<int32_t>(immPtr);
227 case MEI:
228 return decodeImm<int64_t>(immPtr);
230 default:
231 not_reached();
235 // TODO: merge with emitIVA in unit.h
236 size_t encodeVariableSizeImm(int32_t n, unsigned char* buf) {
237 if (LIKELY((n & 0x7f) == n)) {
238 *buf = static_cast<unsigned char>(n) << 1;
239 return 1;
241 assert((n & 0x7fffffff) == n);
242 *reinterpret_cast<uint32_t*>(buf) = (uint32_t(n) << 1) | 0x1;
243 return 4;
246 void encodeIvaToVector(std::vector<uchar>& out, int32_t val) {
247 size_t currentLen = out.size();
248 out.resize(currentLen + 4);
249 out.resize(currentLen + encodeVariableSizeImm(val, &out[currentLen]));
252 int instrLen(const Op* opcode) {
253 auto op = *opcode;
254 int len = 1;
255 int nImm = numImmediates(op);
256 for (int i = 0; i < nImm; i++) {
257 len += immSize(opcode, i);
259 return len;
262 InstrFlags instrFlags(Op opcode) {
263 static const InstrFlags instrFlagsData[] = {
264 #define O(unusedName, unusedImm, unusedPop, unusedPush, flags) flags,
265 OPCODES
266 #undef O
268 return instrFlagsData[uint8_t(opcode)];
271 Offset* instrJumpOffset(Op* instr) {
272 static const int8_t jumpMask[] = {
273 #define NA 0
274 #define MA 0
275 #define IVA 0
276 #define I64A 0
277 #define DA 0
278 #define SA 0
279 #define AA 0
280 #define BA 1
281 #define HA 0
282 #define IA 0
283 #define OA 0
284 #define ONE(a) a
285 #define TWO(a, b) (a + 2 * b)
286 #define THREE(a, b, c) (a + 2 * b + 4 * c)
287 #define FOUR(a, b, c, d) (a + 2 * b + 4 * c + 8 * d)
288 #define O(name, imm, pop, push, flags) imm,
289 OPCODES
290 #undef NA
291 #undef MA
292 #undef IVA
293 #undef I64A
294 #undef DA
295 #undef SA
296 #undef AA
297 #undef HA
298 #undef IA
299 #undef BA
300 #undef OA
301 #undef ONE
302 #undef TWO
303 #undef THREE
304 #undef FOUR
305 #undef O
308 assert(!isSwitch(*instr));
310 if (Op(*instr) == OpIterBreak) {
311 uint32_t veclen = *(uint32_t *)(instr + 1);
312 assert(veclen > 0);
313 Offset* target = (Offset *)((uint32_t *)(instr + 1) + 2 * veclen + 1);
314 return target;
317 int mask = jumpMask[uint8_t(*instr)];
318 if (mask == 0) {
319 return nullptr;
321 int immNum;
322 switch (mask) {
323 case 0: return nullptr;
324 case 1: immNum = 0; break;
325 case 2: immNum = 1; break;
326 case 4: immNum = 2; break;
327 case 8: immNum = 3; break;
328 default: assert(false); return nullptr;
331 return &getImmPtr(instr, immNum)->u_BA;
334 Offset instrJumpTarget(const Op* instrs, Offset pos) {
335 Offset* offset = instrJumpOffset(const_cast<Op*>(instrs + pos));
337 if (!offset) {
338 return InvalidAbsoluteOffset;
339 } else {
340 return *offset + pos;
345 * Return the number of successor-edges including fall-through paths but not
346 * implicit exception paths.
348 int numSuccs(const Op* instr) {
349 if (!instrIsControlFlow(*instr)) return 1;
350 if ((instrFlags(*instr) & TF) != 0) {
351 if (isSwitch(*instr)) {
352 return *(int*)(instr + 1);
354 if (*instr == OpJmp || *instr == OpIterBreak) return 1;
355 return 0;
357 if (instrJumpOffset(const_cast<Op*>(instr))) return 2;
358 return 1;
362 * instrNumPops() returns the number of values consumed from the stack
363 * for a given push/pop instruction. For peek/poke instructions, this
364 * function returns 0.
366 int instrNumPops(const Op* opcode) {
367 static const int8_t numberOfPops[] = {
368 #define NOV 0
369 #define ONE(...) 1
370 #define TWO(...) 2
371 #define THREE(...) 3
372 #define FOUR(...) 4
373 #define LMANY(...) -1
374 #define C_LMANY(...) -2
375 #define V_LMANY(...) -2
376 #define R_LMANY(...) -2
377 #define FMANY -3
378 #define CVMANY -3
379 #define CMANY -3
380 #define O(name, imm, pop, push, flags) pop,
381 OPCODES
382 #undef NOV
383 #undef ONE
384 #undef TWO
385 #undef THREE
386 #undef FOUR
387 #undef LMANY
388 #undef C_LMANY
389 #undef V_LMANY
390 #undef R_LMANY
391 #undef FMANY
392 #undef CVMANY
393 #undef CMANY
394 #undef O
396 int n = numberOfPops[uint8_t(*opcode)];
397 // For most instructions, we know how many values are popped based
398 // solely on the opcode
399 if (n >= 0) return n;
400 // FCall and NewTuple specify how many values are popped in their
401 // first immediate
402 if (n == -3) return getImm(opcode, 0).u_IVA;
403 // For instructions with vector immediates, we have to scan the
404 // contents of the vector immediate to determine how many values
405 // are popped
406 assert(n == -1 || n == -2);
407 ImmVector iv = getImmVector(opcode);
408 // Count the number of values on the stack accounted for by the
409 // ImmVector's location and members
410 int k = iv.numStackValues();
411 // If this instruction also takes a RHS, count that too
412 if (n == -2) ++k;
413 return k;
417 * instrNumPushes() returns the number of values pushed onto the stack
418 * for a given push/pop instruction. For peek/poke instructions or
419 * InsertMid instructions, this function returns 0.
421 int instrNumPushes(const Op* opcode) {
422 static const int8_t numberOfPushes[] = {
423 #define NOV 0
424 #define ONE(...) 1
425 #define TWO(...) 2
426 #define THREE(...) 3
427 #define FOUR(...) 4
428 #define INS_1(...) 0
429 #define INS_2(...) 0
430 #define O(name, imm, pop, push, flags) push,
431 OPCODES
432 #undef NOV
433 #undef ONE
434 #undef TWO
435 #undef THREE
436 #undef FOUR
437 #undef INS_1
438 #undef INS_2
439 #undef O
441 return numberOfPushes[uint8_t(*opcode)];
444 StackTransInfo instrStackTransInfo(const Op* opcode) {
445 static const StackTransInfo::Kind transKind[] = {
446 #define NOV StackTransInfo::Kind::PushPop
447 #define ONE(...) StackTransInfo::Kind::PushPop
448 #define TWO(...) StackTransInfo::Kind::PushPop
449 #define THREE(...) StackTransInfo::Kind::PushPop
450 #define FOUR(...) StackTransInfo::Kind::PushPop
451 #define INS_1(...) StackTransInfo::Kind::InsertMid
452 #define INS_2(...) StackTransInfo::Kind::InsertMid
453 #define O(name, imm, pop, push, flags) push,
454 OPCODES
455 #undef NOV
456 #undef ONE
457 #undef TWO
458 #undef THREE
459 #undef FOUR
460 #undef INS_1
461 #undef INS_2
462 #undef O
464 static const int8_t peekPokeType[] = {
465 #define NOV -1
466 #define ONE(...) -1
467 #define TWO(...) -1
468 #define THREE(...) -1
469 #define FOUR(...) -1
470 #define INS_1(...) 0
471 #define INS_2(...) 1
472 #define O(name, imm, pop, push, flags) push,
473 OPCODES
474 #undef NOV
475 #undef ONE
476 #undef TWO
477 #undef THREE
478 #undef FOUR
479 #undef INS_2
480 #undef INS_1
481 #undef O
483 StackTransInfo ret;
484 ret.kind = transKind[uint8_t(*opcode)];
485 switch (ret.kind) {
486 case StackTransInfo::Kind::PushPop:
487 ret.pos = 0;
488 ret.numPushes = instrNumPushes(opcode);
489 ret.numPops = instrNumPops(opcode);
490 return ret;
491 case StackTransInfo::Kind::InsertMid:
492 ret.numPops = 0;
493 ret.numPushes = 0;
494 ret.pos = peekPokeType[uint8_t(*opcode)];
495 return ret;
496 default:
497 NOT_REACHED();
501 bool pushesActRec(Op opcode) {
502 switch (opcode) {
503 case OpFPushFunc:
504 case OpFPushFuncD:
505 case OpFPushFuncU:
506 case OpFPushObjMethod:
507 case OpFPushObjMethodD:
508 case OpFPushClsMethod:
509 case OpFPushClsMethodF:
510 case OpFPushClsMethodD:
511 case OpFPushCtor:
512 case OpFPushCtorD:
513 case OpFPushCufIter:
514 case OpFPushCuf:
515 case OpFPushCufF:
516 case OpFPushCufSafe:
517 return true;
518 default:
519 return false;
523 static void staticArrayStreamer(ArrayData* ad, std::stringstream& out) {
524 out << "array(";
525 if (!ad->empty()) {
526 bool comma = false;
527 for (ArrayIter it(ad); !it.end(); it.next()) {
528 if (comma) {
529 out << ",";
530 } else {
531 comma = true;
533 Variant key = it.first();
534 // Key.
535 switch (key.getType()) {
536 case KindOfInt64: {
537 out << *key.getInt64Data();
538 break;
540 case KindOfStaticString:
541 case KindOfString: {
542 out << "\""
543 << Util::escapeStringForCPP(key.getStringData()->data(),
544 key.getStringData()->size())
545 << "\"";
546 break;
548 default: assert(false);
550 out << "=>";
551 // Value.
552 Variant val = it.second();
553 switch (val.getType()) {
554 case KindOfUninit:
555 case KindOfNull: {
556 out << "null";
557 break;
559 case KindOfBoolean: {
560 out << (val.toBoolean() ? "true" : "false");
561 break;
563 case KindOfStaticString:
564 case KindOfString: {
565 out << "\""
566 << Util::escapeStringForCPP(val.getStringData()->data(),
567 val.getStringData()->size())
568 << "\"";
569 break;
571 case KindOfInt64: {
572 out << *val.getInt64Data();
573 break;
575 case KindOfDouble: {
576 out << *val.getDoubleData();
577 break;
579 case KindOfArray: {
580 staticArrayStreamer(val.getArrayData(), out);
581 break;
583 default: assert(false);
587 out << ")";
590 void staticStreamer(const TypedValue* tv, std::stringstream& out) {
591 switch (tv->m_type) {
592 case KindOfUninit:
593 case KindOfNull: {
594 out << "null";
595 break;
597 case KindOfBoolean: {
598 out << (tv->m_data.num ? "true" : "false");
599 break;
601 case KindOfStaticString:
602 case KindOfString: {
603 out << "\"" << tv->m_data.pstr->data() << "\"";
604 break;
606 case KindOfInt64: {
607 out << tv->m_data.num;
608 break;
610 case KindOfDouble: {
611 out << tv->m_data.dbl;
612 break;
614 case KindOfArray: {
615 staticArrayStreamer(tv->m_data.parr, out);
616 break;
618 default: assert(false);
622 const char* const locationNames[] = { "L", "C", "H",
623 "GL", "GC",
624 "NL", "NC",
625 "SL", "SC",
626 "R" };
627 const size_t locationNamesCount = sizeof(locationNames) /
628 sizeof(*locationNames);
629 static_assert(locationNamesCount == NumLocationCodes,
630 "Location code missing for locationCodeString");
632 const char* locationCodeString(LocationCode lcode) {
633 assert(lcode >= 0 && lcode < NumLocationCodes);
634 return locationNames[lcode];
637 LocationCode parseLocationCode(const char* s) {
638 if (!*s) return InvalidLocationCode;
640 switch (*s) {
641 case 'L': return LL;
642 case 'C': return LC;
643 case 'H': return LH;
644 case 'R': return LR;
645 default:
646 int incr = (s[1] == 'C');
647 switch (*s) {
648 case 'G': return LocationCode(LGL + incr);
649 case 'N': return LocationCode(LNL + incr);
650 case 'S': return LocationCode(LSL + incr);
652 return InvalidLocationCode;
656 const char* const memberNames[] =
657 { "EC", "PC", "EL", "PL", "ET", "PT", "EI", "W" };
658 const size_t memberNamesCount = sizeof(memberNames) /
659 sizeof(*memberNames);
661 static_assert(memberNamesCount == NumMemberCodes,
662 "Member code missing for memberCodeString");
664 const char* memberCodeString(MemberCode mcode) {
665 assert(mcode >= 0 && mcode < NumMemberCodes);
666 return memberNames[mcode];
669 MemberCode parseMemberCode(const char* s) {
670 int incr;
671 switch (*s) {
672 case 'W': return MW;
673 case 'E': incr = 0; break;
674 case 'P': incr = 1; break;
675 default: return InvalidMemberCode;
677 switch (s[1]) {
678 case 'C': return MemberCode(MEC + incr);
679 case 'L': return MemberCode(MEL + incr);
680 case 'T': return MemberCode(MET + incr);
681 case 'I': return incr ? InvalidMemberCode : MEI;
682 default: return InvalidMemberCode;
686 std::string instrToString(const Op* it, const Unit* u /* = NULL */) {
687 // IncDec names
688 static const char* incdecNames[] = {
689 "PreInc", "PostInc", "PreDec", "PostDec"
691 static const int incdecNamesCount =
692 (int)(sizeof(incdecNames)/sizeof(const char*));
693 // SetOp names
694 static const char* setopNames[] = {
695 #define SETOP_OP(setOpOp, bcOp) #bcOp,
696 SETOP_OPS
697 #undef SETOP_OP
699 static const int setopNamesCount =
700 (int)(sizeof(setopNames)/sizeof(const char*));
702 std::stringstream out;
703 const Op* iStart = it;
704 Op op = *it;
705 ++it;
706 switch (op) {
708 #define READ(t) out << " " << *((t*)&*it); it += sizeof(t)
710 #define READOFF() do { \
711 Offset _value = *(Offset*)it; \
712 out << " " << _value; \
713 if (u != nullptr && _value >= 0) { \
714 out << " (" << u->offsetOf(iStart + _value) << ")"; \
716 it += sizeof(Offset); \
717 } while (false)
719 #define READV() out << " " << decodeVariableSizeImm((const uint8_t**)&it);
721 #define READIVA() do { \
722 out << " "; \
723 auto imm = decodeVariableSizeImm((const uint8_t**)&it); \
724 if (op == OpIncStat && immIdx == 0) { \
725 out << Stats::g_counterNames[imm]; \
726 } else { \
727 out << imm; \
729 immIdx++; \
730 } while (false)
732 #define READOA() do { \
733 int immVal = (int)*((uchar*)&*it); \
734 it += sizeof(uchar); \
735 out << " "; \
736 switch (op) { \
737 case OpIncDecL: case OpIncDecN: case OpIncDecG: case OpIncDecS: \
738 case OpIncDecM: \
739 out << ((immVal >= 0 && immVal < incdecNamesCount) ? \
740 incdecNames[immVal] : "?"); \
741 break; \
742 case OpSetOpL: case OpSetOpN: case OpSetOpG: case OpSetOpS: \
743 case OpSetOpM: \
744 out << ((immVal >=0 && immVal < setopNamesCount) ? \
745 setopNames[immVal] : "?"); \
746 break; \
747 default: \
748 out << immVal; \
749 break; \
751 } while (false)
753 #define READVEC() do { \
754 int sz = *((int*)&*it); \
755 it += sizeof(int) * 2; \
756 const uint8_t* const start = (uint8_t*)it; \
757 out << " <"; \
758 if (sz > 0) { \
759 int immVal = (int)*((uchar*)&*it); \
760 out << ((immVal >= 0 && size_t(immVal) < locationNamesCount) ? \
761 locationCodeString(LocationCode(immVal)) : "?"); \
762 it += sizeof(uchar); \
763 int numLocImms = numLocationCodeImms(LocationCode(immVal)); \
764 for (int i = 0; i < numLocImms; ++i) { \
765 out << ':' << decodeVariableSizeImm((const uint8_t**)&it); \
767 while (reinterpret_cast<const uint8_t*>(it) - start < sz) { \
768 immVal = (int)*((uchar*)&*it); \
769 out << " " << ((immVal >=0 && size_t(immVal) < memberNamesCount) ? \
770 memberCodeString(MemberCode(immVal)) : "?"); \
771 it += sizeof(uchar); \
772 if (memberCodeHasImm(MemberCode(immVal))) { \
773 int64_t imm = decodeMemberCodeImm((const uint8_t**)&it, \
774 MemberCode(immVal)); \
775 out << ':'; \
776 if (memberCodeImmIsString(MemberCode(immVal)) && u) { \
777 const StringData* str = u->lookupLitstrId(imm); \
778 int len = str->size(); \
779 char* escaped = string_addslashes(str->data(), len); \
780 out << '"' << escaped << '"'; \
781 free(escaped); \
782 } else { \
783 out << imm; \
787 assert(reinterpret_cast<const uint8_t*>(it) - start == sz); \
789 out << ">"; \
790 } while (false)
792 #define READLITSTR(sep) do { \
793 Id id = readData<Id>(it); \
794 if (id < 0) { \
795 assert(op == OpSSwitch); \
796 out << sep << "-"; \
797 } else if (u) { \
798 const StringData* sd = u->lookupLitstrId(id); \
799 out << sep << "\"" << \
800 Util::escapeStringForCPP(sd->data(), sd->size()) << "\""; \
801 } else { \
802 out << sep << id; \
804 } while (false)
806 #define READSVEC() do { \
807 int sz = readData<int>(it); \
808 out << " <"; \
809 const char* sep = ""; \
810 for (int i = 0; i < sz; ++i) { \
811 out << sep; \
812 if (op == OpSSwitch) { \
813 READLITSTR(""); \
814 out << ":"; \
816 Offset o = readData<Offset>(it); \
817 if (u != nullptr) { \
818 if (iStart + o == (Op*)u->entry() - 1) { \
819 out << "Invalid"; \
820 } else { \
821 out << u->offsetOf(iStart + o); \
823 } else { \
824 out << o; \
826 sep = " "; \
828 out << ">"; \
829 } while (false)
831 #define READIVEC() do { \
832 int sz = readData<int>(it); \
833 out << " <"; \
834 const char* sep = ""; \
835 for (int i = 0; i < sz; ++i) { \
836 out << sep; \
837 IterKind k = (IterKind)readData<Id>(it); \
838 switch(k) { \
839 case KindOfIter: out << "(Iter) "; break; \
840 case KindOfMIter: out << "(MIter) "; break; \
841 case KindOfCIter: out << "(CIter) "; break; \
843 out << readData<Id>(it); \
844 sep = ", "; \
846 out << ">"; \
847 } while (false)
849 #define ONE(a) H_##a
850 #define TWO(a, b) H_##a; H_##b
851 #define THREE(a, b, c) H_##a; H_##b; H_##c;
852 #define FOUR(a, b, c, d) H_##a; H_##b; H_##c; H_##d;
853 #define NA
854 #define H_MA READVEC()
855 #define H_BLA READSVEC()
856 #define H_SLA READSVEC()
857 #define H_ILA READIVEC()
858 #define H_IVA READIVA()
859 #define H_I64A READ(int64_t)
860 #define H_HA READV()
861 #define H_IA READV()
862 #define H_DA READ(double)
863 #define H_BA READOFF()
864 #define H_OA READOA()
865 #define H_SA READLITSTR(" ")
866 #define H_AA \
867 if (u) { \
868 out << " "; \
869 staticArrayStreamer(u->lookupArrayId(*((Id*)it)), out); \
870 } else { \
871 out << " " << *((Id*)it); \
873 it += sizeof(Id)
874 #define O(name, imm, push, pop, flags) \
875 case Op##name: { \
876 out << #name; \
877 UNUSED unsigned immIdx = 0; \
878 imm; \
879 break; \
881 OPCODES
882 #undef O
883 #undef READ
884 #undef ONE
885 #undef TWO
886 #undef THREE
887 #undef FOUR
888 #undef NA
889 #undef H_MA
890 #undef H_BLA
891 #undef H_SLA
892 #undef H_ILA
893 #undef H_IVA
894 #undef H_I64A
895 #undef H_HA
896 #undef H_IA
897 #undef H_DA
898 #undef H_BA
899 #undef H_OA
900 #undef H_SA
901 #undef H_AA
902 default: assert(false);
904 return out.str();
907 const char* opcodeToName(Op op) {
908 const char* namesArr[] = {
909 #define O(name, imm, inputs, outputs, flags) \
910 #name ,
911 OPCODES
912 #undef O
914 if (op >= Op::LowInvalid && op <= Op::HighInvalid) {
915 return namesArr[uint8_t(op)];
917 return "Invalid";
920 bool instrIsControlFlow(Op opcode) {
921 InstrFlags opFlags = instrFlags(opcode);
922 return (opFlags & CF) != 0;
925 bool instrIsNonCallControlFlow(Op opcode) {
926 if (!instrIsControlFlow(opcode) || isFCallStar(opcode)) return false;
927 switch (opcode) {
928 case OpContEnter:
929 case OpFCallBuiltin:
930 case OpIncl:
931 case OpInclOnce:
932 case OpReq:
933 case OpReqOnce:
934 case OpReqDoc:
935 return false;
937 default:
938 return true;
942 bool instrAllowsFallThru(Op opcode) {
943 InstrFlags opFlags = instrFlags(opcode);
944 return (opFlags & TF) == 0;
947 bool instrReadsCurrentFpi(Op opcode) {
948 InstrFlags opFlags = instrFlags(opcode);
949 return (opFlags & FF) != 0;
952 ImmVector getImmVector(const Op* opcode) {
953 int numImm = numImmediates(*opcode);
954 for (int k = 0; k < numImm; ++k) {
955 ArgType t = immType(*opcode, k);
956 if (t == MA) {
957 void* vp = getImmPtr(opcode, k);
958 return ImmVector::createFromStream(
959 static_cast<const uint8_t*>(vp));
960 } else if (t == BLA || t == SLA || t == ILA) {
961 void* vp = getImmPtr(opcode, k);
962 return ImmVector::createFromStream(
963 static_cast<const int32_t*>(vp));
967 NOT_REACHED();
970 const uint8_t* ImmVector::findLastMember() const {
971 assert(m_length > 0);
973 // Loop that does basically the same as numStackValues(), except
974 // stop at the last.
975 const uint8_t* vec = m_start;
976 const LocationCode locCode = LocationCode(*vec++);
977 const int numLocImms = numLocationCodeImms(locCode);
978 for (int i = 0; i < numLocImms; ++i) {
979 decodeVariableSizeImm(&vec);
982 for (;;) {
983 const uint8_t* ret = vec;
984 MemberCode code = MemberCode(*vec++);
985 if (memberCodeHasImm(code)) {
986 decodeMemberCodeImm(&vec, code);
988 if (vec - m_start == m_length) {
989 return ret;
991 assert(vec - m_start < m_length);
994 NOT_REACHED();
997 bool ImmVector::decodeLastMember(const Unit* u,
998 StringData*& sdOut,
999 MemberCode& membOut,
1000 int64_t* strIdOut /*=NULL*/) const {
1001 const uint8_t* vec = findLastMember();
1002 membOut = MemberCode(*vec++);
1003 if (memberCodeImmIsString(membOut)) {
1004 int64_t strId = decodeMemberCodeImm(&vec, membOut);
1005 if (strIdOut) *strIdOut = strId;
1006 sdOut = u->lookupLitstrId(strId);
1007 return true;
1009 return false;
1013 int instrSpToArDelta(const Op* opcode) {
1014 // This function should only be called for instructions that read
1015 // the current FPI
1016 assert(instrReadsCurrentFpi(*opcode));
1017 // The delta from sp to ar is equal to the number of values on the stack
1018 // that will be consumed by this instruction (numPops) plus the number of
1019 // parameters pushed onto the stack so far that are not being consumed by
1020 // this instruction (numExtra). For the FPass* instructions, numExtra will
1021 // be equal to the first immediate argument (param id). For the FCall
1022 // instructions, numExtra will be 0 because all of the parameters on the
1023 // stack are already accounted for by numPops.
1024 int numPops = instrNumPops(opcode);
1025 int numExtra = isFCallStar(*opcode) ? 0 : getImm(opcode, 0).u_IVA;
1026 int delta = numPops + numExtra;
1027 return delta;
1030 const MInstrInfo& getMInstrInfo(Op op) {
1031 static const MInstrInfo mInstrInfo[] = {
1032 #define MII(instr, attrs, bS, iS, vC, fN) \
1033 {MI_##instr##M, \
1034 {MIA_none, MIA_none, MInstrAttr((attrs) & MIA_base), \
1035 MInstrAttr((attrs) & MIA_base), MInstrAttr((attrs) & MIA_base), \
1036 MInstrAttr((attrs) & MIA_base), MInstrAttr((attrs) & MIA_base), \
1037 MIA_none, \
1038 MIA_none}, \
1039 {MInstrAttr((attrs) & MIA_intermediate), \
1040 MInstrAttr((attrs) & MIA_intermediate), \
1041 MInstrAttr((attrs) & MIA_intermediate), \
1042 MInstrAttr((attrs) & MIA_intermediate), \
1043 MInstrAttr((attrs) & MIA_intermediate), \
1044 MInstrAttr((attrs) & MIA_intermediate), \
1045 MInstrAttr((attrs) & MIA_intermediate), \
1046 MInstrAttr((attrs) & MIA_final)}, \
1047 unsigned(vC), bool((attrs) & MIA_new), bool((attrs) & MIA_final_get), \
1048 #instr},
1049 MINSTRS
1050 #undef MII
1053 switch (op) {
1054 #define MII(instr_, attrs, bS, iS, vC, fN) \
1055 case Op##instr_##M: { \
1056 const MInstrInfo& mii = mInstrInfo[MI_##instr_##M]; \
1057 assert(mii.instr() == MI_##instr_##M); \
1058 return mii; \
1060 MINSTRS
1061 #undef MII
1062 default: not_reached();
1066 ///////////////////////////////////////////////////////////////////////////////