Skip to content

Commit

Permalink
[Perf] More efficient handling of __callStatic
Browse files Browse the repository at this point in the history
Summary: Store HasCall and HasCallStatic flags in ObjectStaticCallbacks for
hphp, in ClassStatement and ClassEvalState for hphpi, and in ClassInfo (as a
last resort) to allow testing without creating an object. Also precompute
UseGet, UseSet, UseIsset and UseUnset for hphpi, rather than calling findMethod
for each one on each object construction.

Test Plan: fast_tests slow_tests

Reviewers: myang

Reviewed By: myang

CC: cbueno, ps, mwilliams, myang

Differential Revision: 344089

Task ID: 746527
  • Loading branch information
mwilliams authored and macvicar committed Oct 18, 2011
1 parent 1d13c6f commit 1139816
Show file tree
Hide file tree
Showing 26 changed files with 275 additions and 244 deletions.
2 changes: 1 addition & 1 deletion src/compiler/analysis/analysis_result.cpp
Expand Up @@ -3682,7 +3682,7 @@ void AnalysisResult::outputCPPRedeclaredClassImpl(CodeGenerator &cg) {
cg_printf("static const RedeclaredObjectStaticCallbacks %s%s = {\n"
" {\n"
" coo_ObjectData,\n"
" 0,0,0,0,&s_%s,0,0,0,0\n"
" 0,0,0,0,&s_%s,0,0,0,0,0\n"
" },\n"
" -1\n"
"};\n",
Expand Down
131 changes: 57 additions & 74 deletions src/compiler/analysis/class_scope.cpp
Expand Up @@ -1344,33 +1344,24 @@ void ClassScope::outputCPPHashTableClasses
void ClassScope::outputCPPClassVarInitImpl
(CodeGenerator &cg, const StringToClassScopePtrVecMap &classScopes,
const vector<const char*> &classes) {
bool system = cg.getOutput() == CodeGenerator::SystemCPP;
if (cg.getOutput() == CodeGenerator::SystemCPP) return;

cg_indentBegin("Variant get%s_class_var_init(CStrRef s, "
"const char *var) {\n",
system ? "_builtin" : "");
bool withEval = !system && Option::EnableEval == Option::FullEval;
if (withEval) {
cg_indentBegin("Variant get_class_var_init(CStrRef s, "
"const char *var) {\n");

if (Option::EnableEval == Option::FullEval) {
// See if there's an eval'd version
cg_indentBegin("{\n");
cg_printf("Variant r;\n");
cg_printf("if (eval_get_class_var_init_hook(r, s, var)) "
"return r;\n");
cg_indentEnd("}\n");
}
if (classes.size()) {
cg_printf("const ObjectStaticCallbacks *cwo = "
"get_%sobject_static_callbacks(s);\n"
"return LIKELY(cwo != 0) ? "
"cwo->os_getInit(var) : throw_missing_class(s);\n",
system ? "builtin_" : "");
} else {
if (system) {
cg_printf("return throw_missing_class(s);\n");
} else {
cg_printf("return get_builtin_class_var_init(s, var);\n");
}
}
cg_printf("const ObjectStaticCallbacks *cwo = "
"get_%sobject_static_callbacks(s);\n"
"return LIKELY(cwo != 0) ? "
"cwo->os_getInit(var) : throw_missing_class(s);\n",
!classes.size() ? "builtin_" : "");
cg_indentEnd("}\n");
}

Expand All @@ -1391,19 +1382,11 @@ void ClassScope::outputCPPDynamicClassCreateImpl
"return r;\n");
cg_indentEnd("}\n");
}
if (!classes.size()) {
if (system) {
cg_printf("return 0;\n");
} else {
cg_printf("return create_builtin_object_only_no_init(s, root);\n");
}
} else {
cg_printf("const ObjectStaticCallbacks *cwo = "
"get_%sobject_static_callbacks(s);\n"
"if (LIKELY(cwo != 0)) return cwo->createOnlyNoInit(root);\n"
"return 0;\n",
system ? "builtin_" : "");
}
cg_printf("const ObjectStaticCallbacks *cwo = "
"get_%sobject_static_callbacks(s);\n"
"if (LIKELY(cwo != 0)) return cwo->createOnlyNoInit(root);\n"
"return 0;\n",
system || !classes.size() ? "builtin_" : "");
cg_indentEnd("}\n");
// output create_object_only()
cg_indentBegin("Object create%s_object_only(CStrRef s, "
Expand All @@ -1421,14 +1404,11 @@ void ClassScope::outputCPPDynamicClassCreateImpl
void ClassScope::outputCPPGetCallInfoStaticMethodImpl(
CodeGenerator &cg, const StringToClassScopePtrVecMap &classScopes,
const vector<const char*> &classes) {
bool system = cg.getOutput() == CodeGenerator::SystemCPP;
bool useHashTable = (classes.size() > 0);
cg_indentBegin("bool get_call_info_static_method%s(MethodCallPackage &mcp)"
" {\n", system ? "_builtin" : "");

cg_printf("StringData *s ATTRIBUTE_UNUSED (mcp.rootCls);\n");
if (cg.getOutput() == CodeGenerator::SystemCPP) return;
cg_indentBegin(
"bool get_call_info_static_method(MethodCallPackage &mcp) {\n");

if (!system && Option::EnableEval == Option::FullEval) {
if (Option::EnableEval == Option::FullEval) {
cg_printf("bool foundClass = false;\n");
cg_printf("if (eval_get_call_info_static_method_hook(mcp, foundClass)) "
"return true;\n");
Expand All @@ -1437,32 +1417,28 @@ void ClassScope::outputCPPGetCallInfoStaticMethodImpl(
cg_printf("return false;\n");
cg_indentEnd("}\n");
}
if (useHashTable) {
cg_printf("const ObjectStaticCallbacks *cwo = "
"get_%sobject_static_callbacks(s);\n"
"return ObjectStaticCallbacks::GetCallInfo(cwo,mcp,-1);\n" ,
system ? "builtin_" : "");
} else {
if (system) {
cg_printf("return ObjectStaticCallbacks::GetCallInfo(0,mcp,-1);\n");
} else {
cg_printf("return get_call_info_static_method_builtin(mcp);\n");
}
}

cg_printf("StringData *s ATTRIBUTE_UNUSED (mcp.rootCls);\n");

cg_printf("const ObjectStaticCallbacks *cwo = "
"get_%sobject_static_callbacks(s);\n"
"if (LIKELY(cwo != 0)) "
"return ObjectStaticCallbacks::GetCallInfo(cwo,mcp,-1);\n"
"if (mcp.m_fatal) throw_missing_class(s->data());\n"
"return false;\n",
!classes.size() ? "builtin_" : "");

cg_indentEnd("}\n");
}


void ClassScope::outputCPPGetStaticPropertyImpl
(CodeGenerator &cg, const StringToClassScopePtrVecMap &classScopes,
const vector<const char*> &classes) {
bool system = cg.getOutput() == CodeGenerator::SystemCPP;
if (cg.getOutput() == CodeGenerator::SystemCPP) return;

cg_indentBegin("Variant get%s_static_property(CStrRef s, "
"const char *prop) {\n",
system ? "_builtin" : "");
if (!system && Option::EnableEval == Option::FullEval) {
cg_indentBegin("Variant get_static_property(CStrRef s, "
"const char *prop) {\n");
if (Option::EnableEval == Option::FullEval) {
// See if there's an eval'd version
cg_indentBegin("{\n");
cg_printf("Variant r;\n");
Expand All @@ -1473,16 +1449,14 @@ void ClassScope::outputCPPGetStaticPropertyImpl

cg.printf("const ObjectStaticCallbacks * cwo = "
"get%s_object_static_callbacks(s);\n",
system ? "_builtin" : "");
!classes.size() ? "_builtin" : "");
cg.printf("if (cwo) return cwo->os_get(prop);\n");

cg_printf("return null;\n");
cg_indentEnd("}\n");

cg_indentBegin("Variant *get%s_static_property_lv(CStrRef s, "
"const char *prop) {\n",
system ? "_builtin" : "");
if (!system && Option::EnableEval == Option::FullEval) {
cg_indentBegin("Variant *get_static_property_lv(CStrRef s, "
"const char *prop) {\n");
if (Option::EnableEval == Option::FullEval) {
// See if there's an eval'd version
cg_indentBegin("{\n");
cg_printf("Variant *r;\n");
Expand All @@ -1493,20 +1467,18 @@ void ClassScope::outputCPPGetStaticPropertyImpl

cg.printf("const ObjectStaticCallbacks * cwo = "
"get%s_object_static_callbacks(s);\n",
system ? "_builtin" : "");
!classes.size() ? "_builtin" : "");
cg.printf("if (cwo) return &cwo->os_lval(prop);\n");

cg_printf("return NULL;\n");
cg_indentEnd("}\n");
}

void ClassScope::outputCPPGetClassConstantImpl
(CodeGenerator &cg, const StringToClassScopePtrVecMap &classScopes) {
bool system = cg.getOutput() == CodeGenerator::SystemCPP;
cg_indentBegin("Variant get%s_class_constant(CStrRef s, "
"const char *constant, int fatal /* = true */) {\n",
system ? "_builtin" : "");
if (!system && Option::EnableEval == Option::FullEval) {
if (cg.getOutput() == CodeGenerator::SystemCPP) return;
cg_indentBegin("Variant get_class_constant(CStrRef s, "
"const char *constant, int fatal /* = true */) {\n");
if (Option::EnableEval == Option::FullEval) {
// See if there's an eval'd version
cg_indentBegin("{\n");
cg_printf("Variant r;\n");
Expand All @@ -1518,7 +1490,7 @@ void ClassScope::outputCPPGetClassConstantImpl
cg.indentBegin("{\n");
cg.printf("const ObjectStaticCallbacks * cwo = "
"get%s_object_static_callbacks(s);\n",
system ? "_builtin" : "");
!classScopes.size() ? "_builtin" : "");
cg.printf("if (cwo) return cwo->os_constant(constant);\n");
cg.indentEnd("}\n");

Expand Down Expand Up @@ -2731,7 +2703,7 @@ void ClassScope::outputCPPGlobalTableWrappersImpl(CodeGenerator &cg,
cg_indentBegin("{\n");
}
if (isInterface()) {
cg_printf("0,0,0,0,0,0,0,0,0,0\n");
cg_printf("0,0,0,0,0,0,0,0,0,0,0\n");
} else {
cg_printf("(ObjectData*(*)(ObjectData*))%s%s,\n",
Option::CreateObjectOnlyPrefix, id.c_str());
Expand Down Expand Up @@ -2777,11 +2749,22 @@ void ClassScope::outputCPPGlobalTableWrappersImpl(CodeGenerator &cg,

if (derivesFromRedeclaring() != DirectFromRedeclared &&
(par = getParentScope(ar))) {
cg_printf("&%s%s\n",
cg_printf("&%s%s",
Option::ClassStaticsCallbackPrefix, par->getId().c_str());
} else {
cg_printf("0\n");
cg_printf("0");
}

int attributes = 0;
if (m_attribute & (HasUnknownStaticMethodHandler|
InheritsUnknownStaticMethodHandler)) {
attributes |= ObjectData::HasCallStatic;
}
if (m_attribute & (HasUnknownMethodHandler|
InheritsUnknownMethodHandler)) {
attributes |= ObjectData::HasCall;
}
cg_printf(",0x%x\n", attributes);
}

if (isRedeclaring()) {
Expand Down
4 changes: 2 additions & 2 deletions src/runtime/base/builtin_functions.cpp
Expand Up @@ -504,7 +504,7 @@ Variant o_invoke_failed(const char *cls, const char *meth,

Array collect_few_args(int count, INVOKE_FEW_ARGS_IMPL_ARGS) {
if (RuntimeOption::UseArgArray) {
if (count == 0) return Array();
if (count == 0) return Array::Create();
ArgArray *args = NEW(ArgArray)(count);
ArgArray::Argument *argp = args->getStack();
if (count > 0) {
Expand Down Expand Up @@ -556,7 +556,7 @@ Array collect_few_args(int count, INVOKE_FEW_ARGS_IMPL_ARGS) {
}
switch (count) {
case 0: {
return Array();
return Array::Create();
}
case 1: {
return Array(ArrayInit(1).set(a0).create());
Expand Down
2 changes: 2 additions & 0 deletions src/runtime/base/class_info.h
Expand Up @@ -49,6 +49,8 @@ class ClassInfo {
IsPrivate = (1 << 8), // x x
IsStatic = (1 << 9), // x x
IsInherited = (1 << 10), // x
HasCall = IsPublic, // x
HasCallStatic = IsProtected,// x

IsReference = (1 << 11), // x x x
IsOptional = (1 << 12), // x
Expand Down
6 changes: 0 additions & 6 deletions src/runtime/base/externals.h
Expand Up @@ -56,9 +56,7 @@ extern const ObjectStaticCallbacks *
* Getting a static property
*/
extern Variant get_static_property(CStrRef s, const char *prop);
extern Variant get_builtin_static_property(CStrRef s, const char *prop);
extern Variant *get_static_property_lv(CStrRef s, const char *prop);
extern Variant *get_builtin_static_property_lv(CStrRef s, const char *prop);

// defined in builtin_functions.cpp
Variant &get_static_property_lval(CStrRef s, const char *prop);
Expand All @@ -67,7 +65,6 @@ Variant &get_static_property_lval(CStrRef s, const char *prop);
* Getting the init value of a class variable
*/
extern Variant get_class_var_init(CStrRef s, const char *var);
extern Variant get_builtin_class_var_init(CStrRef s, const char *var);

/**
* Getting a constant
Expand All @@ -87,8 +84,6 @@ extern ConstantType check_constant(CStrRef name);
*/
extern Variant get_class_constant(CStrRef s, const char *prop,
int fatal = true);
extern Variant get_builtin_class_constant(CStrRef s, const char *prop,
int fatal = true);

/**
* Getting function info
Expand All @@ -100,7 +95,6 @@ extern bool get_call_info_no_eval(const CallInfo *&ci, void *&extra,
extern bool get_call_info_builtin(const CallInfo *&ci, void *&extra,
const char *s, int64 hash = -1);
extern bool get_call_info_static_method(MethodCallPackage &info);
extern bool get_call_info_static_method_builtin(MethodCallPackage &info);

/**
* Class/function meta info entirely encoded here as a const char * array.
Expand Down
45 changes: 32 additions & 13 deletions src/runtime/base/object_data.cpp
Expand Up @@ -56,6 +56,9 @@ ObjectData::~ObjectData() {
}
}

CallInfo *ObjectData::GetCallHandler() {
return &s_ObjectData_call_handler;
}

static CallInfo s_ObjectData_null_constructor(
(void*)ObjectData::NullConstructor,
Expand Down Expand Up @@ -529,11 +532,12 @@ inline ALWAYS_INLINE bool GetCallInfoHelper(bool ex, const char *cls,
return true;
}
} else {
const ObjectStaticCallbacks *cur = osc;
do {
if (!ex || found || !strcasecmp(cls, osc->cls->data())) {
if (!ex || found || !strcasecmp(cls, cur->cls->data())) {
if (ex) found = true;
if (const int *ix = osc->mcit_ix) {
const MethodCallInfoTable *info = osc->mcit;
if (const int *ix = cur->mcit_ix) {
const MethodCallInfoTable *info = cur->mcit;

int h = hash & ix[0];
int o = ix[h + 1];
Expand All @@ -551,20 +555,20 @@ inline ALWAYS_INLINE bool GetCallInfoHelper(bool ex, const char *cls,
}
}
}
if (!osc->parent) {
if (LIKELY(!osc->redeclaredParent)) break;
if (!cur->parent) {
if (LIKELY(!cur->redeclaredParent)) break;
if (LIKELY(!globals)) {
globals = (char*)get_global_variables();
}
osc = *(ObjectStaticCallbacks**)(globals + osc->redeclaredParent);
cur = *(ObjectStaticCallbacks**)(globals + cur->redeclaredParent);
if (mcp.obj) {
mcp.obj = static_cast<DynamicObjectData*>(mcp.obj)->
getRedeclaredParent();
}
} else {
osc = osc->parent;
cur = cur->parent;
}
} while (osc);
} while (cur);
}

if (mcp.obj) {
Expand All @@ -578,12 +582,17 @@ inline ALWAYS_INLINE bool GetCallInfoHelper(bool ex, const char *cls,
StrNR cls = mcp.rootCls;
bool ok = false;
if (!obj || !obj->o_instanceof(cls)) {
obj = create_object_only_no_init(cls);
if (obj) {
ok = obj->hasCallStatic();
obj->release();
obj = 0;
if (LIKELY(osc != 0)) {
ok = osc->checkAttribute(ObjectData::HasCallStatic);
} else {
const ClassInfo *info = ClassInfo::FindClassInterfaceOrTrait(cls);
if (info) {
ok = info->getAttribute() & ClassInfo::HasCallStatic;
} else if (mcp.m_fatal) {
throw_missing_class(cls);
}
}
obj = 0;
} else {
ok = obj->hasCallStatic() || obj->hasCall();
}
Expand Down Expand Up @@ -611,6 +620,16 @@ bool ObjectStaticCallbacks::GetCallInfoEx(const char *cls,
return GetCallInfoHelper(true, cls, osc, mcp, hash);
}

bool ObjectStaticCallbacks::checkAttribute(int attrs) const {
if (attributes & attrs) return true;
if (LIKELY(!redeclaredParent)) return false;

const ObjectStaticCallbacks *osc =
*(ObjectStaticCallbacks**)((char*)get_global_variables() +
redeclaredParent);
return osc->checkAttribute(attrs);
}

Variant ObjectData::os_invoke(CStrRef c, CStrRef s,
CArrRef params, int64 hash,
bool fatal /* = true */) {
Expand Down

0 comments on commit 1139816

Please sign in to comment.