Add inlining support to the tracelet region selector
[hiphop-php.git] / hphp / runtime / vm / jit / annotation.cpp
blob79b44e327a3c03aa13bbd92748fa6e747f2fa9a2
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/jit/annotation.h"
18 #include "hphp/runtime/vm/jit/normalized-instruction.h"
19 #include "hphp/runtime/vm/jit/translator.h"
20 #include "hphp/runtime/vm/jit/translator-inline.h"
21 #include "hphp/util/base.h"
23 namespace HPHP {
24 namespace Transl {
26 TRACE_SET_MOD(trans);
29 * A mapping from FCall instructions to the statically-known StringData*
30 * that they're calling. Used to accelerate our FCall translations.
32 enum class CallRecordType {
33 EncodedNameAndArgs,
34 Function
36 struct CallRecord {
37 CallRecordType m_type;
38 union {
39 const StringData* m_encodedName;
40 const Func* m_func;
44 typedef hphp_hash_map<SrcKey,CallRecord,SrcKey::Hasher> CallDB;
45 static CallDB s_callDB;
47 static const StringData*
48 encodeCallAndArgs(const StringData* name, int numArgs) {
49 char numArgsBuf[16];
50 snprintf(numArgsBuf, 15, "@%d@", numArgs);
51 String s = String(numArgsBuf) + String(name->data());
52 return StringData::GetStaticString(s.get());
55 static void
56 decodeNameAndArgs(const StringData* enc, string& outName, int& outNumArgs) {
57 const char* numArgs = strchr(enc->data(), '@');
58 assert(numArgs && *numArgs =='@');
59 numArgs++;
60 outNumArgs = atoi(numArgs);
61 const char* name = strchr(numArgs, '@');
62 assert(name && *name == '@');
63 name++;
64 outName = name;
67 static void recordNameAndArgs(const SrcKey sk,
68 const StringData* name,
69 int numArgs) {
70 CallRecord cr;
71 cr.m_type = CallRecordType::EncodedNameAndArgs;
72 cr.m_encodedName = encodeCallAndArgs(name, numArgs);
73 s_callDB.insert(std::make_pair(sk, cr));
76 static void recordFunc(const SrcKey sk,
77 const Func* func) {
78 FTRACE(2, "annotation: recordFunc: {}@{} {}\n",
79 sk.unit()->filepath()->data(),
80 sk.offset(),
81 func->fullName()->data());
83 CallRecord cr;
84 cr.m_type = CallRecordType::Function;
85 cr.m_func = func;
86 s_callDB.insert(std::make_pair(sk, cr));
89 static void recordActRecPush(const SrcKey sk,
90 const StringData* name,
91 const StringData* clsName,
92 bool staticCall) {
93 auto unit = sk.unit();
94 FTRACE(2, "annotation: recordActRecPush: {}@{} {}{}{} ({}static)\n",
95 unit->filepath()->data(),
96 sk.offset(),
97 clsName ? clsName->data() : "",
98 clsName ? "::" : "",
99 name,
100 !staticCall ? "non" : "");
102 SrcKey next(sk);
103 next.advance(unit);
104 const FPIEnt *fpi = sk.func()->findFPI(next.offset());
105 assert(fpi);
106 assert(name->isStatic());
107 assert(sk.offset() == fpi->m_fpushOff);
108 auto const fcall = SrcKey { sk.func(), fpi->m_fcallOff };
109 assert(isFCallStar(toOp(*unit->at(fcall.offset()))));
110 if (clsName) {
111 const Class* cls = Unit::lookupUniqueClass(clsName);
112 bool magic = false;
113 Class* ctx = sk.func()->cls();
114 const Func* func = lookupImmutableMethod(cls, name, magic,
115 staticCall, ctx);
116 if (func) {
117 recordFunc(fcall, func);
119 return;
121 const Func* func = Unit::lookupFunc(name);
122 if (func && func->isNameBindingImmutable(unit)) {
123 // this will never go into a call cache, so we dont need to
124 // encode the args. it will be used in OpFCall below to
125 // set the i->funcd.
126 recordFunc(fcall, func);
127 } else {
128 // It's not enough to remember the function name; we also need to encode
129 // the number of arguments and current flag disposition.
130 int numArgs = getImm((Op*)unit->at(sk.offset()), 0).u_IVA;
131 recordNameAndArgs(fcall, name, numArgs);
135 void annotate(NormalizedInstruction* i) {
136 switch(i->op()) {
137 case OpFPushObjMethodD:
138 case OpFPushClsMethodD:
139 case OpFPushClsMethodF:
140 case OpFPushCtorD:
141 case OpFPushCtor:
142 case OpFPushFuncD: {
143 // When we push predictable action records, we can use a simpler
144 // translation for their corresponding FCall.
145 const StringData* className = nullptr;
146 const StringData* funcName = nullptr;
147 if (i->op() == OpFPushFuncD) {
148 funcName = i->m_unit->lookupLitstrId(i->imm[1].u_SA);
149 } else if (i->op() == OpFPushObjMethodD) {
150 if (i->inputs[0]->valueType() != KindOfObject) break;
151 const Class* cls = i->inputs[0]->rtt.valueClass();
152 if (!cls) break;
153 funcName = i->m_unit->lookupLitstrId(i->imm[1].u_SA);
154 className = cls->name();
155 } else if (i->op() == OpFPushClsMethodF) {
156 if (!i->inputs[1]->isString() ||
157 i->inputs[1]->rtt.valueString() == nullptr ||
158 i->inputs[0]->valueType() != KindOfClass) {
159 break;
161 const Class* cls = i->inputs[0]->rtt.valueClass();
162 if (!cls) break;
163 funcName = i->inputs[1]->rtt.valueString();
164 className = cls->name();
165 } else if (i->op() == OpFPushClsMethodD) {
166 funcName = i->m_unit->lookupLitstrId(i->imm[1].u_SA);
167 className = i->m_unit->lookupLitstrId(i->imm[2].u_SA);
168 } else if (i->op() == OpFPushCtorD) {
169 className = i->m_unit->lookupLitstrId(i->imm[1].u_SA);
170 const Class* cls = Unit::lookupUniqueClass(className);
171 if (!cls) break;
172 funcName = cls->getCtor()->name();
173 } else {
174 assert(i->op() == OpFPushCtor);
175 const Class* cls = i->inputs[0]->rtt.valueClass();
176 if (!cls) break;
177 funcName = cls->getCtor()->name();
179 assert(funcName->isStatic());
180 recordActRecPush(i->source, funcName, className,
181 i->op() == OpFPushClsMethodD ||
182 i->op() == OpFPushClsMethodF);
183 } break;
184 case OpFCall:
185 case OpFCallArray: {
186 CallRecord callRec;
187 if (mapGet(s_callDB, i->source, &callRec)) {
188 if (callRec.m_type == CallRecordType::Function) {
189 i->funcd = callRec.m_func;
190 } else {
191 assert(callRec.m_type == CallRecordType::EncodedNameAndArgs);
192 i->funcName = callRec.m_encodedName;
194 } else {
195 i->funcName = nullptr;
197 } break;
198 default: break;
202 const StringData*
203 fcallToFuncName(const NormalizedInstruction* i) {
204 CallRecord callRec;
205 if (mapGet(s_callDB, i->source, &callRec)) {
206 if (callRec.m_type == CallRecordType::Function) {
207 return callRec.m_func->name();
209 string name;
210 int numArgs;
211 decodeNameAndArgs(callRec.m_encodedName, name, numArgs);
212 return StringData::GetStaticString(name.c_str());
214 return nullptr;