2 +----------------------------------------------------------------------+
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"
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
{
37 CallRecordType m_type
;
39 const StringData
* m_encodedName
;
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
) {
50 snprintf(numArgsBuf
, 15, "@%d@", numArgs
);
51 String s
= String(numArgsBuf
) + String(name
->data());
52 return StringData::GetStaticString(s
.get());
56 decodeNameAndArgs(const StringData
* enc
, string
& outName
, int& outNumArgs
) {
57 const char* numArgs
= strchr(enc
->data(), '@');
58 assert(numArgs
&& *numArgs
=='@');
60 outNumArgs
= atoi(numArgs
);
61 const char* name
= strchr(numArgs
, '@');
62 assert(name
&& *name
== '@');
67 static void recordNameAndArgs(const SrcKey sk
,
68 const StringData
* name
,
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
,
78 FTRACE(2, "annotation: recordFunc: {}@{} {}\n",
79 sk
.unit()->filepath()->data(),
81 func
->fullName()->data());
84 cr
.m_type
= CallRecordType::Function
;
86 s_callDB
.insert(std::make_pair(sk
, cr
));
89 static void recordActRecPush(const SrcKey sk
,
90 const StringData
* name
,
91 const StringData
* clsName
,
93 auto unit
= sk
.unit();
94 FTRACE(2, "annotation: recordActRecPush: {}@{} {}{}{} ({}static)\n",
95 unit
->filepath()->data(),
97 clsName
? clsName
->data() : "",
100 !staticCall
? "non" : "");
104 const FPIEnt
*fpi
= sk
.func()->findFPI(next
.offset());
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()))));
111 const Class
* cls
= Unit::lookupUniqueClass(clsName
);
113 Class
* ctx
= sk
.func()->cls();
114 const Func
* func
= lookupImmutableMethod(cls
, name
, magic
,
117 recordFunc(fcall
, func
);
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
126 recordFunc(fcall
, func
);
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
) {
137 case OpFPushObjMethodD
:
138 case OpFPushClsMethodD
:
139 case OpFPushClsMethodF
:
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();
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
) {
161 const Class
* cls
= i
->inputs
[0]->rtt
.valueClass();
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
);
172 funcName
= cls
->getCtor()->name();
174 assert(i
->op() == OpFPushCtor
);
175 const Class
* cls
= i
->inputs
[0]->rtt
.valueClass();
177 funcName
= cls
->getCtor()->name();
179 assert(funcName
->isStatic());
180 recordActRecPush(i
->source
, funcName
, className
,
181 i
->op() == OpFPushClsMethodD
||
182 i
->op() == OpFPushClsMethodF
);
187 if (mapGet(s_callDB
, i
->source
, &callRec
)) {
188 if (callRec
.m_type
== CallRecordType::Function
) {
189 i
->funcd
= callRec
.m_func
;
191 assert(callRec
.m_type
== CallRecordType::EncodedNameAndArgs
);
192 i
->funcName
= callRec
.m_encodedName
;
195 i
->funcName
= nullptr;
203 fcallToFuncName(const NormalizedInstruction
* i
) {
205 if (mapGet(s_callDB
, i
->source
, &callRec
)) {
206 if (callRec
.m_type
== CallRecordType::Function
) {
207 return callRec
.m_func
->name();
211 decodeNameAndArgs(callRec
.m_encodedName
, name
, numArgs
);
212 return StringData::GetStaticString(name
.c_str());