Opimize re-entry path

Use the jitted prologs, and add a "few args" re-entry
point to avoid the overhead of creating an Array
Esse commit está contido em:
Mark Williams
2013-05-05 17:13:26 -07:00
commit de Sara Golemon
commit aacaa37ad5
33 arquivos alterados com 563 adições e 423 exclusões
+6 -6
Ver Arquivo
@@ -113,7 +113,7 @@ void ArrayIter::objInit(ObjectData *obj) {
}
default: {
assert(obj->instanceof(SystemLib::s_IteratorClass));
obj->o_invoke(s_rewind, Array());
obj->o_invoke_few_args(s_rewind, 0);
break;
}
}
@@ -171,7 +171,7 @@ bool ArrayIter::endHelper() {
}
default: {
ObjectData* obj = getIteratorObj();
return !obj->o_invoke(s_valid, Array());
return !obj->o_invoke_few_args(s_valid, 0);
}
}
}
@@ -215,7 +215,7 @@ void ArrayIter::nextHelper() {
}
default:
ObjectData* obj = getIteratorObj();
obj->o_invoke(s_next, Array());
obj->o_invoke_few_args(s_next, 0);
}
}
@@ -248,7 +248,7 @@ Variant ArrayIter::firstHelper() {
}
default: {
ObjectData* obj = getIteratorObj();
return obj->o_invoke(s_key, Array());
return obj->o_invoke_few_args(s_key, 0);
}
}
}
@@ -295,7 +295,7 @@ Variant ArrayIter::second() {
}
default: {
ObjectData* obj = getIteratorObj();
return obj->o_invoke(s_current, Array());
return obj->o_invoke_few_args(s_current, 0);
}
}
}
@@ -340,7 +340,7 @@ void ArrayIter::secondHelper(Variant& v) {
}
default: {
ObjectData* obj = getIteratorObj();
v = obj->o_invoke(s_current, Array());
v = obj->o_invoke_few_args(s_current, 0);
break;
}
}
+9 -6
Ver Arquivo
@@ -241,9 +241,11 @@ struct ElmUCompare {
const CallCtx* ctx;
bool operator()(ElmT left, ElmT right) const {
Variant ret;
g_vmContext->invokeFunc(ret.asTypedValue(), *ctx,
CREATE_VECTOR2(acc.getValue(left),
acc.getValue(right)));
TypedValue args[2];
tvDup(acc.getValue(left).asTypedValue(), args+0);
tvDup(acc.getValue(right).asTypedValue(), args+1);
g_vmContext->invokeFuncFew(ret.asTypedValue(), *ctx,
2, args);
if (ret.isInteger()) {
return ret.toInt64() < 0;
}
@@ -269,9 +271,10 @@ struct ElmUCompare {
return false;
}
Variant ret2;
g_vmContext->invokeFunc(ret.asTypedValue(), *ctx,
CREATE_VECTOR2(acc.getValue(right),
acc.getValue(left)));
tvDup(acc.getValue(right).asTypedValue(), args+0);
tvDup(acc.getValue(left).asTypedValue(), args+1);
g_vmContext->invokeFuncFew(ret.asTypedValue(), *ctx,
2, args);
if (ret2.isBoolean()) {
return ret2.toBoolean();
}
+3 -3
Ver Arquivo
@@ -884,7 +884,7 @@ bool empty(CVarRef v, CVarRef offset) {
return collectionOffsetEmpty(obj, offset);
} else {
if (!Variant::GetArrayAccess(tva)->
o_invoke(s_offsetExists, Array::Create(offset))) {
o_invoke_few_args(s_offsetExists, 1, offset)) {
return true;
}
return empty(v.rvalAt(offset));
@@ -928,7 +928,7 @@ bool isset(CVarRef v, int64_t offset) {
return collectionOffsetIsset(obj, offset);
} else {
return Variant::GetArrayAccess(tva)->
o_invoke(s_offsetExists, Array::Create(offset), -1);
o_invoke_few_args(s_offsetExists, 1, offset);
}
}
if (Variant::IsString(tva)) {
@@ -956,7 +956,7 @@ bool isset(CVarRef v, CVarRef offset) {
return collectionOffsetIsset(obj, offset);
} else {
return Variant::GetArrayAccess(tva)->
o_invoke(s_offsetExists, Array::Create(offset), -1);
o_invoke_few_args(s_offsetExists, 1, offset);
}
}
if (Variant::IsString(tva)) {
+24 -14
Ver Arquivo
@@ -572,7 +572,6 @@ public:
TypedValue* lookupClsCns(const StringData* cls,
const StringData* cns);
ActRec* arGetSfp(const ActRec* ar);
// Get the next outermost VM frame, even accross re-entry
ActRec* getOuterVMFrame(const ActRec* ar);
@@ -676,24 +675,16 @@ public:
CVarRef getEvaledArg(const StringData* val);
private:
void enterVMWork(ActRec* enterFnAr);
void enterVM(TypedValue* retval,
ActRec* ar,
ExtraArgs* extraArgs);
void reenterVM(TypedValue* retval,
ActRec* ar,
ExtraArgs* extraArgs,
TypedValue* savedSP);
void enterVMPrologue(ActRec* enterFnAr);
void enterVM(TypedValue* retval, ActRec* ar);
void reenterVM(TypedValue* retval, ActRec* ar, TypedValue* savedSP);
void doFPushCuf(PC& pc, bool forward, bool safe);
void unwindBuiltinFrame();
template <bool forwarding>
void pushClsMethodImpl(VM::Class* cls, StringData* name,
ObjectData* obj, int numArgs);
template <bool reenter>
bool prepareFuncEntry(ActRec* ar,
PC& pc,
ExtraArgs* extraArgs);
bool prepareArrayArgs(ActRec* ar, ArrayData* args,
ExtraArgs*& extraArgs);
bool prepareFuncEntry(ActRec* ar, PC& pc);
bool prepareArrayArgs(ActRec* ar, ArrayData* args);
void recordCodeCoverage(PC pc);
bool isReturnHelper(uintptr_t address);
int m_coverPrevLine;
@@ -723,6 +714,25 @@ public:
invokeFunc(retval, ctx.func, params, ctx.this_, ctx.cls, varEnv,
ctx.invName, InvokeIgnoreByRefErrors);
}
void invokeFuncFew(TypedValue* retval,
const HPHP::VM::Func* f,
void* thisOrCls,
StringData* invName,
int argc, TypedValue* argv);
void invokeFuncFew(TypedValue* retval,
const HPHP::VM::Func* f,
void* thisOrCls,
StringData* invName = nullptr) {
invokeFuncFew(retval, f, thisOrCls, invName, 0, nullptr);
}
void invokeFuncFew(TypedValue* retval,
const CallCtx& ctx,
int argc, TypedValue* argv) {
invokeFuncFew(retval, ctx.func,
ctx.this_ ? (void*)ctx.this_ :
ctx.cls ? (char*)ctx.cls + 1 : nullptr,
ctx.invName, argc, argv);
}
void invokeContFunc(const HPHP::VM::Func* f,
ObjectData* this_,
TypedValue* param = nullptr);
+1 -2
Ver Arquivo
@@ -52,8 +52,7 @@ UserFile::UserFile(VM::Class *cls, int options /*= 0 */,
m_obj = VM::Instance::newInstance(cls);
m_obj.o_set("context", context);
Variant ret;
g_vmContext->invokeFunc(ret.asTypedValue(), ctor,
Array::Create(), m_obj.get());
g_vmContext->invokeFuncFew(ret.asTypedValue(), ctor, m_obj.get());
m_StreamOpen = lookupMethod(s_stream_open.get());
m_StreamClose = lookupMethod(s_stream_close.get());
+63 -68
Ver Arquivo
@@ -87,7 +87,7 @@ void ObjectData::destruct() {
tvWriteNull(&retval);
try {
// Call the destructor method
g_vmContext->invokeFunc(&retval, meth, null_array, this);
g_vmContext->invokeFuncFew(&retval, meth, this);
} catch (...) {
// Swallow any exceptions that escape the __destruct method
handle_destructor_exception();
@@ -142,7 +142,7 @@ Object ObjectData::iterableObject(bool& isIterable,
}
Object obj(this);
while (obj->instanceof(SystemLib::s_IteratorAggregateClass)) {
Variant iterator = obj->o_invoke(s_getIterator, Array());
Variant iterator = obj->o_invoke_few_args(s_getIterator, 0);
if (!iterator.isObject()) break;
ObjectData* o = iterator.getObjectData();
if (o->instanceof(SystemLib::s_IteratorClass)) {
@@ -469,86 +469,79 @@ Array ObjectData::o_toIterArray(CStrRef context,
return Array(retval);
}
Variant ObjectData::o_invoke(CStrRef s, CArrRef params,
strhash_t hash /* = -1 */,
bool fatal /* = true */) {
static bool decode_invoke(CStrRef s, ObjectData* obj, bool fatal,
CallCtx& ctx) {
// TODO This duplicates some logic from vm_decode_function and
// vm_call_user_func, we should refactor this in the near future
ObjectData* this_ = this;
HPHP::VM::Class* cls = getVMClass();
StringData* invName = nullptr;
ctx.this_ = obj;
ctx.cls = obj->getVMClass();
ctx.invName = nullptr;
// XXX The lookup below doesn't take context into account, so it will lead
// to incorrect behavior in some corner cases. o_invoke is gradually being
// removed from the HPHP runtime this should be ok for the short term.
const HPHP::VM::Func* f = cls->lookupMethod(s.get());
if (f && (f->attrs() & AttrStatic)) {
// If we found a method and its static, null out this_
this_ = nullptr;
} else if (!f) {
if (this_) {
// If this_ is non-null AND we could not find a method, try
// looking up __call in cls's method table
f = cls->lookupMethod(s___call.get());
ctx.func = ctx.cls->lookupMethod(s.get());
if (ctx.func) {
if (ctx.func->attrs() & AttrStatic) {
// If we found a method and its static, null out this_
ctx.this_ = nullptr;
}
if (!f) {
} else {
// If this_ is non-null AND we could not find a method, try
// looking up __call in cls's method table
ctx.func = ctx.cls->lookupMethod(s___call.get());
if (!ctx.func) {
// Bail if we couldn't find the method or __call
o_invoke_failed(o_getClassName().data(), s.data(), fatal);
return uninit_null();
o_invoke_failed(ctx.cls->name()->data(), s.data(), fatal);
return false;
}
// We found __call! Stash the original name into invName.
assert(!(f->attrs() & AttrStatic));
invName = s.get();
invName->incRefCount();
assert(!(ctx.func->attrs() & AttrStatic));
ctx.invName = s.get();
ctx.invName->incRefCount();
}
assert(f);
return true;
}
Variant ObjectData::o_invoke(CStrRef s, CArrRef params,
bool fatal /* = true */) {
CallCtx ctx;
if (!decode_invoke(s, this, fatal, ctx)) return Variant(Variant::nullInit);
Variant ret;
g_vmContext->invokeFunc((TypedValue*)&ret, f, params, this_, cls,
nullptr, invName);
g_vmContext->invokeFunc((TypedValue*)&ret, ctx, params);
return ret;
}
#define APPEND_1_ARGS(params) params.append(a0);
#define APPEND_2_ARGS(params) APPEND_1_ARGS(params); params.append(a1)
#define APPEND_3_ARGS(params) APPEND_2_ARGS(params); params.append(a2)
#define APPEND_4_ARGS(params) APPEND_3_ARGS(params); params.append(a3)
#define APPEND_5_ARGS(params) APPEND_4_ARGS(params); params.append(a4)
#define APPEND_6_ARGS(params) APPEND_5_ARGS(params); params.append(a5)
#define APPEND_7_ARGS(params) APPEND_6_ARGS(params); params.append(a6)
#define APPEND_8_ARGS(params) APPEND_7_ARGS(params); params.append(a7)
#define APPEND_9_ARGS(params) APPEND_8_ARGS(params); params.append(a8)
#define APPEND_10_ARGS(params) APPEND_9_ARGS(params); params.append(a9)
Variant ObjectData::o_invoke_few_args(CStrRef s, strhash_t hash, int count,
Variant ObjectData::o_invoke_few_args(CStrRef s, int count,
INVOKE_FEW_ARGS_IMPL_ARGS) {
Array params = Array::Create();
CallCtx ctx;
if (!decode_invoke(s, this, true, ctx)) return Variant(Variant::nullInit);
TypedValue args[INVOKE_FEW_ARGS_COUNT];
switch(count) {
case 1: APPEND_1_ARGS(params);
break;
case 2: APPEND_2_ARGS(params);
break;
case 3: APPEND_3_ARGS(params);
break;
#if INVOKE_FEW_ARGS_COUNT > 3
case 4: APPEND_4_ARGS(params);
break;
case 5: APPEND_5_ARGS(params);
break;
case 6: APPEND_6_ARGS(params);
break;
#if INVOKE_FEW_ARGS_COUNT > 6
case 7: APPEND_7_ARGS(params);
break;
case 8: APPEND_8_ARGS(params);
break;
case 9: APPEND_9_ARGS(params);
break;
case 10: APPEND_10_ARGS(params);
break;
#endif
#endif
default: not_implemented();
#if INVOKE_FEW_ARGS_COUNT > 6
case 10: tvDup(a9.asTypedValue(), args + 9);
case 9: tvDup(a8.asTypedValue(), args + 8);
case 8: tvDup(a7.asTypedValue(), args + 7);
case 7: tvDup(a6.asTypedValue(), args + 6);
#endif
#if INVOKE_FEW_ARGS_COUNT > 3
case 6: tvDup(a5.asTypedValue(), args + 5);
case 5: tvDup(a4.asTypedValue(), args + 4);
case 4: tvDup(a3.asTypedValue(), args + 3);
#endif
case 3: tvDup(a2.asTypedValue(), args + 2);
case 2: tvDup(a1.asTypedValue(), args + 1);
case 1: tvDup(a0.asTypedValue(), args + 0);
case 0: break;
}
return o_invoke(s, params, hash);
Variant ret;
g_vmContext->invokeFuncFew((TypedValue*)&ret, ctx, count, args);
return ret;
}
bool ObjectData::php_sleep(Variant &ret) {
@@ -576,7 +569,7 @@ void ObjectData::serializeImpl(VariableSerializer *serializer) const {
if (instanceof(SystemLib::s_SerializableClass)) {
assert(!isCollection());
Variant ret =
const_cast<ObjectData*>(this)->o_invoke(s_serialize, Array(), -1);
const_cast<ObjectData*>(this)->o_invoke_few_args(s_serialize, 0);
if (ret.isString()) {
serializer->writeSerializableObject(o_getClassName(), ret.toString());
} else if (ret.isNull()) {
@@ -594,7 +587,7 @@ void ObjectData::serializeImpl(VariableSerializer *serializer) const {
assert(!isCollection());
try {
Variant ret =
const_cast<ObjectData*>(this)->o_invoke(s_serialize, Array(), -1);
const_cast<ObjectData*>(this)->o_invoke_few_args(s_serialize, 0);
if (ret.isString()) {
serializer->writeSerializableObject(o_getClassName(), ret.toString());
} else if (ret.isNull()) {
@@ -735,8 +728,10 @@ Variant ObjectData::offsetGet(Variant key) {
return uninit_null();
}
Variant v;
g_vmContext->invokeFunc((TypedValue*)(&v), method,
CREATE_VECTOR1(key), this);
TypedValue args[1];
tvDup(key.asTypedValue(), args + 0);
g_vmContext->invokeFuncFew((TypedValue*)(&v), method,
this, nullptr, 1, args);
return v;
}
+2 -3
Ver Arquivo
@@ -180,9 +180,8 @@ class ObjectData : public CountableNF {
static Object FromArray(ArrayData *properties);
// method invocation with CStrRef
Variant o_invoke(CStrRef s, CArrRef params, strhash_t hash = -1,
bool fatal = true);
Variant o_invoke_few_args(CStrRef s, strhash_t hash, int count,
Variant o_invoke(CStrRef s, CArrRef params, bool fatal = true);
Variant o_invoke_few_args(CStrRef s, int count,
INVOKE_FEW_ARGS_DECL_ARGS);
// misc
+19 -21
Ver Arquivo
@@ -1840,7 +1840,7 @@ void Variant::callOffsetUnset(CVarRef key) {
if (LIKELY(obj->isCollection())) {
collectionOffsetUnset(obj, key);
} else {
getArrayAccess()->o_invoke(s_offsetUnset, Array::Create(key));
getArrayAccess()->o_invoke_few_args(s_offsetUnset, 1, key);
}
}
@@ -1863,8 +1863,8 @@ static void raise_bad_offset_notice() {
if (obj->isCollection()) { \
return collectionOffsetGet(obj, offset); \
} else { \
return getArrayAccess()->o_invoke(s_offsetGet, \
Array::Create(offset)); \
return getArrayAccess()->o_invoke_few_args(s_offsetGet, \
1, offset); \
} \
break; \
} \
@@ -1896,7 +1896,7 @@ Variant Variant::rvalAtHelper(int64_t offset, ACCESSPARAMS_IMPL) const {
if (LIKELY(obj->isCollection())) {
return collectionOffsetGet(obj, offset);
} else {
return getArrayAccess()->o_invoke(s_offsetGet, Array::Create(offset));
return getArrayAccess()->o_invoke_few_args(s_offsetGet, 1, offset);
}
break;
}
@@ -1942,7 +1942,7 @@ Variant Variant::rvalAt(CStrRef offset, ACCESSPARAMS_IMPL) const {
if (LIKELY(obj->isCollection())) {
return collectionOffsetGet(obj, offset);
} else {
return getArrayAccess()->o_invoke(s_offsetGet, Array::Create(offset));
return getArrayAccess()->o_invoke_few_args(s_offsetGet, 1, offset);
}
break;
}
@@ -2008,7 +2008,7 @@ Variant Variant::rvalAt(CVarRef offset, ACCESSPARAMS_IMPL) const {
if (LIKELY(obj->isCollection())) {
return collectionOffsetGet(obj, offset);
} else {
return getArrayAccess()->o_invoke(s_offsetGet, Array::Create(offset));
return getArrayAccess()->o_invoke_few_args(s_offsetGet, 1, offset);
}
break;
}
@@ -2039,7 +2039,7 @@ CVarRef Variant::rvalRefHelper(T offset, CVarRef tmp, ACCESSPARAMS_IMPL) const {
return collectionOffsetGet(obj, offset);
} else {
const_cast<Variant&>(tmp) =
getArrayAccess()->o_invoke(s_offsetGet, Array::Create(offset));
getArrayAccess()->o_invoke_few_args(s_offsetGet, 1, offset);
return tmp;
}
break;
@@ -2315,9 +2315,8 @@ Variant &Variant::lvalAt() {
if (obj->isCollection()) {
raise_error("Cannot use [] for reading");
}
Array params = CREATE_VECTOR1(uninit_null());
Variant& ret = lvalBlackHole();
ret = m_data.pobj->o_invoke(s_offsetGet, params);
ret = m_data.pobj->o_invoke_few_args(s_offsetGet, 1, init_null_variant);
raise_warning("Indirect modification of overloaded element of %s has "
"no effect", m_data.pobj->o_getClassName().data());
return ret;
@@ -2398,23 +2397,23 @@ Variant Variant::o_setRef(CStrRef propName, CVarRef val,
return m_data.pobj->o_setRef(propName, val, context);
}
Variant Variant::o_invoke(CStrRef s, CArrRef params, int64_t hash /* = -1 */) {
Variant Variant::o_invoke(CStrRef s, CArrRef params) {
if (m_type == KindOfObject) {
return m_data.pobj->o_invoke(s, params, hash);
return m_data.pobj->o_invoke(s, params);
} else if (m_type == KindOfRef) {
return m_data.pref->var()->o_invoke(s, params, hash);
return m_data.pref->var()->o_invoke(s, params);
} else {
throw_call_non_object(s.c_str());
}
}
Variant Variant::o_invoke_few_args(CStrRef s, int64_t hash, int count,
Variant Variant::o_invoke_few_args(CStrRef s, int count,
INVOKE_FEW_ARGS_IMPL_ARGS) {
if (m_type == KindOfObject) {
return m_data.pobj->o_invoke_few_args(s, hash, count,
return m_data.pobj->o_invoke_few_args(s, count,
INVOKE_FEW_ARGS_PASS_ARGS);
} else if (m_type == KindOfRef) {
return m_data.pref->var()->o_invoke_few_args(s, hash, count,
return m_data.pref->var()->o_invoke_few_args(s, count,
INVOKE_FEW_ARGS_PASS_ARGS);
} else {
throw_call_non_object(s.c_str());
@@ -2476,7 +2475,7 @@ inline ALWAYS_INLINE CVarRef Variant::SetImpl(Variant *self, T key,
if (obj->isCollection()) {
collectionOffsetSet(obj, key, v);
} else {
self->getArrayAccess()->o_invoke_few_args(s_offsetSet, -1, 2, key, v);
self->getArrayAccess()->o_invoke_few_args(s_offsetSet, 2, key, v);
}
break;
}
@@ -2533,8 +2532,7 @@ CVarRef Variant::append(CVarRef v) {
if (LIKELY(obj->isCollection())) {
collectionOffsetAppend(obj, v);
} else {
Array params = CREATE_VECTOR2(uninit_null(), v);
obj->o_invoke(s_offsetSet, params);
obj->o_invoke_few_args(s_offsetSet, 2, init_null_variant, v);
}
break;
}
@@ -2602,7 +2600,7 @@ inline ALWAYS_INLINE CVarRef Variant::SetRefImpl(Variant *self, T key,
if (self->m_data.pobj->isCollection()) {
raise_error("An element of a collection cannot be taken by reference");
}
self->getArrayAccess()->o_invoke_few_args(s_offsetSet, -1, 2, key, v);
self->getArrayAccess()->o_invoke_few_args(s_offsetSet, 2, key, v);
break;
}
default:
@@ -2658,7 +2656,7 @@ CVarRef Variant::appendRef(CVarRef v) {
if (LIKELY(obj->isCollection())) {
raise_error("Collection elements cannot be taken by reference");
} else {
obj->o_invoke_few_args(s_offsetSet, -1, 2, uninit_null(), v);
obj->o_invoke_few_args(s_offsetSet, 2, uninit_null(), v);
}
}
case KindOfStaticString:
@@ -3147,7 +3145,7 @@ void Variant::unserialize(VariableUnserializer *uns,
if (!obj->instanceof(SystemLib::s_SerializableClass)) {
raise_error("%s didn't implement Serializable", clsName.data());
}
obj->o_invoke(s_unserialize, CREATE_VECTOR1(serialized), -1);
obj->o_invoke_few_args(s_unserialize, 1, serialized);
obj.get()->clearNoDestruct();
} catch (ClassNotFoundException &e) {
if (!uns->allowUnknownSerializableClass()) {
+2 -2
Ver Arquivo
@@ -892,8 +892,8 @@ class Variant : private TypedValue {
}
Variant o_setRef(CStrRef s, CVarRef v, CStrRef context = null_string);
Variant o_invoke(CStrRef s, CArrRef params, int64_t hash = -1);
Variant o_invoke_few_args(CStrRef s, int64_t hash, int count,
Variant o_invoke(CStrRef s, CArrRef params);
Variant o_invoke_few_args(CStrRef s, int count,
INVOKE_FEW_ARGS_DECL_ARGS);
template <typename T>
+2 -3
Ver Arquivo
@@ -28,12 +28,11 @@ static StaticString s_write("write");
///////////////////////////////////////////////////////////////////////////////
String PhpThriftBuffer::readImpl() {
Array args(CREATE_VECTOR1(m_size));
return m_xin->o_invoke(s_read, args, -1);
return m_xin->o_invoke_few_args(s_read, 1, m_size);
}
void PhpThriftBuffer::flushImpl(CStrRef data) {
m_xout->o_invoke(s_write, CREATE_VECTOR1(data), -1);
m_xout->o_invoke_few_args(s_write, 1, data);
}
void PhpThriftBuffer::throwError(const char *msg, int code) {
+1 -1
Ver Arquivo
@@ -378,7 +378,7 @@ void VariableSerializer::write(CObjRef v) {
if (v.instanceof(SystemLib::s_JsonSerializableClass)) {
assert(!v->isCollection());
Variant ret = v->o_invoke(s_jsonSerialize, null_array, -1);
Variant ret = v->o_invoke_few_args(s_jsonSerialize, 0);
// for non objects or when $this is returned
if (!ret.isObject() || (ret.isObject() && !ret.same(v))) {
write(ret);
+4 -4
Ver Arquivo
@@ -81,7 +81,7 @@ void CmdUser::invokeList(DebuggerClient *client, const std::string &cls) {
pclient->m_client = client;
try {
Object cmd = create_object(cls.c_str(), null_array);
cmd->o_invoke(s_onAutoComplete, CREATE_VECTOR1(pclient), -1);
cmd->o_invoke_few_args(s_onAutoComplete, 1, pclient);
} catch (...) {}
}
@@ -90,7 +90,7 @@ bool CmdUser::invokeHelp(DebuggerClient *client, const std::string &cls) {
pclient->m_client = client;
try {
Object cmd = create_object(cls.c_str(), null_array);
Variant ret = cmd->o_invoke(s_help, CREATE_VECTOR1(pclient), -1);
Variant ret = cmd->o_invoke_few_args(s_help, 1, pclient);
return !same(ret, false);
} catch (...) {}
return false;
@@ -101,7 +101,7 @@ bool CmdUser::invokeClient(DebuggerClient *client, const std::string &cls) {
pclient->m_client = client;
try {
Object cmd = create_object(cls.c_str(), null_array);
Variant ret = cmd->o_invoke(s_onClient, CREATE_VECTOR1(pclient), -1);
Variant ret = cmd->o_invoke_few_args(s_onClient, 1, pclient);
return !same(ret, false);
} catch (...) {}
return false;
@@ -112,7 +112,7 @@ bool CmdUser::onServer(DebuggerProxy *proxy) {
p_DebuggerProxyCmdUser pproxy(NEWOBJ(c_DebuggerProxyCmdUser)());
pproxy->m_proxy = proxy;
try {
Variant ret = m_cmd->o_invoke(s_onServer, CREATE_VECTOR1(pproxy), -1);
Variant ret = m_cmd->o_invoke_few_args(s_onServer, 1, pproxy);
return !same(ret, false);
} catch (...) {}
return false;
+2 -2
Ver Arquivo
@@ -740,10 +740,10 @@ int DebuggerProxy::getStackDepth() {
VMExecutionContext* context = g_vmContext;
ActRec *fp = context->getFP();
if (!fp) return 0;
ActRec *prev = context->arGetSfp(fp);
ActRec *prev = fp->arGetSfp();
while (fp != prev) {
fp = prev;
prev = context->arGetSfp(fp);
prev = fp->arGetSfp();
depth++;
}
return depth;
+13 -6
Ver Arquivo
@@ -163,7 +163,9 @@ Variant f_array_fill(int start_index, int num, CVarRef value) {
static bool filter_func(CVarRef value, const void *data) {
CallCtx* ctx = (CallCtx*)data;
Variant ret;
g_vmContext->invokeFunc((TypedValue*)&ret, *ctx, CREATE_VECTOR1(value));
TypedValue args[1];
tvDup(value.asTypedValue(), args + 0);
g_vmContext->invokeFuncFew((TypedValue*)&ret, *ctx, 1, args);
return ret.toBoolean();
}
Variant f_array_filter(CVarRef input, CVarRef callback /* = null_variant */) {
@@ -459,8 +461,10 @@ Variant f_array_rand(CVarRef input, int num_req /* = 1 */) {
static Variant reduce_func(CVarRef result, CVarRef operand, const void *data) {
CallCtx* ctx = (CallCtx*)data;
Variant ret;
g_vmContext->invokeFunc((TypedValue*)&ret, *ctx,
CREATE_VECTOR2(result, operand));
TypedValue args[2];
tvDup(result.asTypedValue(), args + 0);
tvDup(operand.asTypedValue(), args + 1);
g_vmContext->invokeFuncFew((TypedValue*)&ret, *ctx, 2, args);
return ret;
}
Variant f_array_reduce(CVarRef input, CVarRef callback,
@@ -565,8 +569,11 @@ static void walk_func(VRefParam value, CVarRef key, CVarRef userdata,
const void *data) {
CallCtx* ctx = (CallCtx*)data;
Variant sink;
g_vmContext->invokeFunc((TypedValue*)&sink, *ctx,
CREATE_VECTOR3(ref(value), key, userdata));
TypedValue args[3];
tvDup(value->asTypedValue(), args + 0);
tvDup(key.asTypedValue(), args + 1);
tvDup(userdata.asTypedValue(), args + 2);
g_vmContext->invokeFuncFew((TypedValue*)&sink, *ctx, 3, args);
}
bool f_array_walk_recursive(VRefParam input, CVarRef funcname,
CVarRef userdata /* = null_variant */) {
@@ -653,7 +660,7 @@ int64_t f_count(CVarRef var, bool recursive /* = false */) {
{
Object obj = var.toObject();
if (obj.instanceof(SystemLib::s_CountableClass)) {
return obj->o_invoke(s_count, null_array, -1);
return obj->o_invoke_few_args(s_count, 0);
}
}
break;
+2 -2
Ver Arquivo
@@ -300,12 +300,12 @@ Variant f_get_object_vars(CVarRef object) {
Variant f_call_user_method_array(CStrRef method_name, VRefParam obj,
CArrRef paramarr) {
return obj.toObject()->o_invoke(method_name, paramarr, -1);
return obj.toObject()->o_invoke(method_name, paramarr);
}
Variant f_call_user_method(int _argc, CStrRef method_name, VRefParam obj,
CArrRef _argv /* = null_array */) {
return obj.toObject()->o_invoke(method_name, _argv, -1);
return obj.toObject()->o_invoke(method_name, _argv);
}
///////////////////////////////////////////////////////////////////////////////
+30 -17
Ver Arquivo
@@ -522,7 +522,9 @@ Object c_Vector::t_map(CVarRef callback) {
for (uint i = 0; i < m_size; ++i) {
TypedValue* tv = &vec->m_data[i];
int32_t version = m_version;
g_vmContext->invokeFunc(tv, ctx, CREATE_VECTOR1(tvAsCVarRef(&m_data[i])));
TypedValue args[1];
tvDup(&m_data[i], args + 0);
g_vmContext->invokeFuncFew(tv, ctx, 1, args);
if (UNLIKELY(version != m_version)) {
tvRefcountedDecRef(tv);
throw_collection_modified();
@@ -545,8 +547,9 @@ Object c_Vector::t_filter(CVarRef callback) {
for (uint i = 0; i < m_size; ++i) {
Variant ret;
int32_t version = m_version;
g_vmContext->invokeFunc(ret.asTypedValue(), ctx,
CREATE_VECTOR1(tvAsCVarRef(&m_data[i])));
TypedValue args[1];
tvDup(&m_data[i], args + 0);
g_vmContext->invokeFuncFew(ret.asTypedValue(), ctx, 1, args);
if (UNLIKELY(version != m_version)) {
throw_collection_modified();
}
@@ -1463,7 +1466,9 @@ Object c_Map::t_map(CVarRef callback) {
}
TypedValue* tv = &np.data;
int32_t version = m_version;
g_vmContext->invokeFunc(tv, ctx, CREATE_VECTOR1(tvAsCVarRef(&p.data)));
TypedValue args[1];
tvDup(&p.data, args + 0);
g_vmContext->invokeFuncFew(tv, ctx, 1, args);
if (UNLIKELY(version != m_version)) {
tvRefcountedDecRef(tv);
throw_collection_modified();
@@ -1493,8 +1498,9 @@ Object c_Map::t_filter(CVarRef callback) {
if (!p.validValue()) continue;
Variant ret;
int32_t version = m_version;
g_vmContext->invokeFunc(ret.asTypedValue(), ctx,
CREATE_VECTOR1(tvAsCVarRef(&p.data)));
TypedValue args[1];
tvDup(&p.data, args + 0);
g_vmContext->invokeFuncFew(ret.asTypedValue(), ctx, 1, args);
if (UNLIKELY(version != m_version)) {
throw_collection_modified();
}
@@ -2629,8 +2635,9 @@ Object c_StableMap::t_map(CVarRef callback) {
for (Bucket* p = m_pListHead; p; p = p->pListNext) {
Variant ret;
int32_t version = m_version;
g_vmContext->invokeFunc(ret.asTypedValue(), ctx,
CREATE_VECTOR1(tvAsCVarRef(&p->data)));
TypedValue args[1];
tvDup(&p->data, args + 0);
g_vmContext->invokeFuncFew(ret.asTypedValue(), ctx, 1, args);
if (UNLIKELY(version != m_version)) {
throw_collection_modified();
}
@@ -2671,8 +2678,9 @@ Object c_StableMap::t_filter(CVarRef callback) {
for (Bucket* p = m_pListHead; p; p = p->pListNext) {
Variant ret;
int32_t version = m_version;
g_vmContext->invokeFunc(ret.asTypedValue(), ctx,
CREATE_VECTOR1(tvAsCVarRef(&p->data)));
TypedValue args[1];
tvDup(&p->data, args + 0);
g_vmContext->invokeFuncFew(ret.asTypedValue(), ctx, 1, args);
if (UNLIKELY(version != m_version)) {
throw_collection_modified();
}
@@ -3616,7 +3624,9 @@ Object c_Set::t_map(CVarRef callback) {
}
TypedValue* tv = &np.data;
int32_t version = m_version;
g_vmContext->invokeFunc(tv, ctx, CREATE_VECTOR1(tvAsCVarRef(&p.data)));
TypedValue args[1];
tvDup(&p.data, args + 0);
g_vmContext->invokeFuncFew(tv, ctx, 1, args);
if (UNLIKELY(version != m_version)) {
tvRefcountedDecRef(tv);
throw_collection_modified();
@@ -3642,8 +3652,9 @@ Object c_Set::t_filter(CVarRef callback) {
if (!p.validValue()) continue;
Variant ret;
int32_t version = m_version;
g_vmContext->invokeFunc(ret.asTypedValue(), ctx,
CREATE_VECTOR1(tvAsCVarRef(&p.data)));
TypedValue args[1];
tvDup(&p.data, args + 0);
g_vmContext->invokeFuncFew(ret.asTypedValue(), ctx, 1, args);
if (UNLIKELY(version != m_version)) {
throw_collection_modified();
}
@@ -4352,8 +4363,9 @@ Object c_Pair::t_map(CVarRef callback) {
Object obj = vec = NEWOBJ(c_Vector)();
vec->reserve(2);
for (uint64_t i = 0; i < 2; ++i) {
g_vmContext->invokeFunc(&vec->m_data[i], ctx,
CREATE_VECTOR1(tvAsCVarRef(&getElms()[i])));
TypedValue args[1];
tvDup(&getElms()[i], args + 0);
g_vmContext->invokeFuncFew(&vec->m_data[i], ctx, 1, args);
++vec->m_size;
}
return obj;
@@ -4371,8 +4383,9 @@ Object c_Pair::t_filter(CVarRef callback) {
Object obj = vec = NEWOBJ(c_Vector)();
for (uint64_t i = 0; i < 2; ++i) {
Variant ret;
g_vmContext->invokeFunc(ret.asTypedValue(), ctx,
CREATE_VECTOR1(tvAsCVarRef(&getElms()[i])));
TypedValue args[1];
tvDup(&getElms()[i], args + 0);
g_vmContext->invokeFuncFew(ret.asTypedValue(), ctx, 1, args);
if (ret.toBoolean()) {
vec->add(&getElms()[i]);
}
+1 -1
Ver Arquivo
@@ -122,7 +122,7 @@ void c_Continuation::t_next() {
static StaticString s_next("next");
void c_Continuation::t_rewind() {
this->o_invoke(s_next, Array());
this->o_invoke_few_args(s_next, 0);
}
bool c_Continuation::t_valid() {
+9 -8
Ver Arquivo
@@ -170,10 +170,10 @@ void c_UConverter::ucnvToUCallback(c_UConverter *objval,
UConverterCallbackReason reason,
UErrorCode *pErrorCode) {
String source(args->source, args->sourceLimit - args->source, CopyString);
VRefParam errRef((long)*pErrorCode);
Variant ret = objval->o_invoke(s_toUCallback, CREATE_VECTOR4(
reason, source, String(codeUnits, length, CopyString), ref(errRef)
), -1);
Variant errRef((int64_t)*pErrorCode);
Variant ret = objval->o_invoke_few_args(
s_toUCallback, 4,
reason, source, String(codeUnits, length, CopyString), strongBind(errRef));
if (errRef.is(KindOfInt64)) {
*pErrorCode = (UErrorCode)errRef.toInt64();
} else {
@@ -229,10 +229,11 @@ void c_UConverter::ucnvFromUCallback(c_UConverter *objval,
U16_NEXT(codeUnits, i, length, c);
source.append((int64_t)c);
}
VRefParam errRef((int64_t)*pErrorCode);
Variant ret = objval->o_invoke(s_fromUCallback, CREATE_VECTOR4(
reason, source, (int64_t)codePoint, ref(errRef)
), -1);
Variant errRef((int64_t)*pErrorCode);
Variant ret =
objval->o_invoke_few_args(
s_fromUCallback, 4,
reason, source, (int64_t)codePoint, strongBind(errRef));
if (errRef.is(KindOfInt64)) {
*pErrorCode = (UErrorCode)errRef.toInt64();
} else {
+2 -3
Ver Arquivo
@@ -1833,7 +1833,7 @@ static bool do_fetch(sp_PDOStatement stmt, bool do_bind, Variant &ret,
if (!stmt->fetch.constructor.empty() &&
(flags & PDO_FETCH_PROPS_LATE)) {
ret.asCObjRef().get()->o_invoke(stmt->fetch.constructor,
stmt->fetch.ctor_args, -1);
stmt->fetch.ctor_args);
ret.asCObjRef().get()->clearNoDestruct();
}
}
@@ -1969,8 +1969,7 @@ static bool do_fetch(sp_PDOStatement stmt, bool do_bind, Variant &ret,
case PDO_FETCH_CLASS:
if (!stmt->fetch.constructor.empty() &&
!(flags & (PDO_FETCH_PROPS_LATE | PDO_FETCH_SERIALIZE))) {
ret.toObject()->o_invoke(stmt->fetch.constructor, stmt->fetch.ctor_args,
-1);
ret.toObject()->o_invoke(stmt->fetch.constructor, stmt->fetch.ctor_args);
ret.toObject()->clearNoDestruct();
}
if (flags & PDO_FETCH_CLASSTYPE) {
+1 -1
Ver Arquivo
@@ -875,7 +875,7 @@ Variant f_hphp_invoke_method(CVarRef obj, CStrRef cls, CStrRef name,
return invoke_static_method(cls, name, params);
}
ObjectData *o = obj.toCObjRef().get();
return o->o_invoke(name, params, -1);
return o->o_invoke(name, params);
}
bool f_hphp_instanceof(CObjRef obj, CStrRef name) {
+1 -1
Ver Arquivo
@@ -475,7 +475,7 @@ static bool do_request(c_SoapClient *client, xmlDoc *request,
if (client->m_trace) {
client->m_last_request = String((char*)buf, buf_size, CopyString);
}
response = client->o_invoke_few_args(s___dorequest, -1, 5,
response = client->o_invoke_few_args(s___dorequest, 5,
String(buf, buf_size, AttachLiteral),
String(location, AttachLiteral),
String(action, AttachLiteral),
+11 -11
Ver Arquivo
@@ -207,14 +207,14 @@ Variant f_iterator_apply(CVarRef obj, CVarRef func,
return false;
}
Object pobj = obj.toObject();
pobj->o_invoke(s_rewind, null_array, -1);
pobj->o_invoke_few_args(s_rewind, 0);
int64_t count = 0;
while (same(pobj->o_invoke(s_valid, null_array, -1), true)) {
while (same(pobj->o_invoke_few_args(s_valid, 0), true)) {
if (!same(vm_call_user_func(func, params), true)) {
break;
}
++count;
pobj->o_invoke(s_next, null_array, -1);
pobj->o_invoke_few_args(s_next, 0);
}
return count;
}
@@ -224,11 +224,11 @@ Variant f_iterator_count(CVarRef obj) {
return false;
}
Object pobj = obj.toObject();
pobj->o_invoke(s_rewind, null_array, -1);
pobj->o_invoke_few_args(s_rewind, 0);
int64_t count = 0;
while (same(pobj->o_invoke(s_valid, null_array, -1), true)) {
while (same(pobj->o_invoke_few_args(s_valid, 0), true)) {
++count;
pobj->o_invoke(s_next, null_array, -1);
pobj->o_invoke_few_args(s_next, 0);
}
return count;
}
@@ -239,16 +239,16 @@ Variant f_iterator_to_array(CVarRef obj, bool use_keys /* = true */) {
}
Array ret(Array::Create());
Object pobj = obj.toObject();
pobj->o_invoke(s_rewind, null_array, -1);
while (same(pobj->o_invoke(s_valid, null_array, -1), true)) {
Variant val = pobj->o_invoke(s_current, null_array, -1);
pobj->o_invoke_few_args(s_rewind, 0);
while (same(pobj->o_invoke_few_args(s_valid, 0), true)) {
Variant val = pobj->o_invoke_few_args(s_current, 0);
if (use_keys) {
Variant key = pobj->o_invoke(s_key, null_array, -1);
Variant key = pobj->o_invoke_few_args(s_key, 0);
ret.set(key, val);
} else {
ret.append(val);
}
pobj->o_invoke(s_next, null_array, -1);
pobj->o_invoke_few_args(s_next, 0);
}
return ret;
}
+1 -1
Ver Arquivo
@@ -336,7 +336,7 @@ static Variant xml_call_handler(XmlParser *parser, CVarRef handler,
retval = invoke(handler.toString().c_str(), args, -1);
} else {
retval = parser->object.toObject()->
o_invoke(handler.toString(), args, -1);
o_invoke(handler.toString(), args);
}
} else if (handler.isArray() && handler.getArrayData()->size() == 2 &&
(handler[0].isString() || handler[0].isObject()) &&
+8 -11
Ver Arquivo
@@ -116,7 +116,7 @@ protected:
buffer_used = 0;
buffer_size = _buffer_size;
p = _p;
t = p->o_invoke(s_getTransport, Array(), -1);
t = p->o_invoke_few_args(s_getTransport, 0);
}
~PHPTransport() {
free(buffer);
@@ -134,7 +134,7 @@ protected:
class PHPOutputTransport : public PHPTransport {
public:
PHPOutputTransport(CObjRef _p, size_t _buffer_size = 8192) {
explicit PHPOutputTransport(CObjRef _p, size_t _buffer_size = 8192) {
construct_with_zval(_p, _buffer_size);
}
@@ -204,17 +204,16 @@ public:
protected:
void directFlush() {
t->o_invoke(s_flush, Array(), -1);
t->o_invoke_few_args(s_flush, 0);
}
void directWrite(const char* data, size_t len) {
Array args = CREATE_VECTOR1(String(data, len, CopyString));
t->o_invoke(s_write, args, -1);
t->o_invoke_few_args(s_write, 1, String(data, len, CopyString));
}
};
class PHPInputTransport : public PHPTransport {
public:
PHPInputTransport(Object _p, size_t _buffer_size = 8192) {
explicit PHPInputTransport(Object _p, size_t _buffer_size = 8192) {
construct_with_zval(_p, _buffer_size);
}
@@ -236,9 +235,8 @@ public:
void put_back() {
if (buffer_used) {
t->o_invoke(s_putBack,
CREATE_VECTOR1(String(buffer_ptr, buffer_used, CopyString)),
-1);
t->o_invoke_few_args(s_putBack,
1, String(buffer_ptr, buffer_used, CopyString));
}
buffer_used = 0;
buffer_ptr = buffer;
@@ -299,8 +297,7 @@ public:
protected:
void refill() {
assert(buffer_used == 0);
String ret = t->o_invoke(s_read,
CREATE_VECTOR1((int64_t)buffer_size), -1).toString();
String ret = t->o_invoke_few_args(s_read, 1, (int64_t)buffer_size);
buffer_used = ret.size();
memcpy(buffer, ret.data(), buffer_used);
buffer_ptr = buffer;
+243 -180
Ver Arquivo
@@ -107,6 +107,16 @@ struct VMPrepareUnwind : std::exception {
}
ActRec* ActRec::arGetSfp() const {
ActRec* prevFrame = (ActRec*)m_savedRbp;
if (LIKELY(((uintptr_t)prevFrame - Util::s_stackLimit) >=
Util::s_stackSize)) {
if (LIKELY(prevFrame != nullptr)) return prevFrame;
}
return const_cast<ActRec*>(this);
}
bool
ActRec::skipFrame() const {
return m_func && m_func->isBuiltin();
@@ -327,8 +337,8 @@ void VarEnv::attach(ActRec* fp) {
this,
isGlobalScope() ? "global scope" : "local scope",
int(fp->m_func->numNamedLocals()), fp);
assert(m_depth == 0 || g_vmContext->arGetSfp(fp) == m_cfp ||
(g_vmContext->arGetSfp(fp) == fp && g_vmContext->isNested()));
assert(m_depth == 0 || fp->arGetSfp() == m_cfp ||
(fp->arGetSfp() == fp && g_vmContext->isNested()));
m_cfp = fp;
m_depth++;
@@ -1105,7 +1115,7 @@ UnwindStatus Stack::unwindFrame(ActRec*& fp, int offset, PC& pc, Fault fault) {
return UnwindResumeVM;
}
ActRec *prevFp = context->arGetSfp(fp);
ActRec *prevFp = fp->arGetSfp();
SKTRACE(1, sk, "unwindFrame: fp %p prevFp %p\n",
fp, prevFp);
if (LIKELY(!fp->m_func->isGenerator())) {
@@ -1156,7 +1166,7 @@ TypedValue* Stack::frameStackBase(const ActRec* fp) {
TypedValue* Stack::generatorStackBase(const ActRec* fp) {
assert(fp->m_func->isGenerator());
VMExecutionContext* context = g_vmContext;
ActRec* sfp = context->arGetSfp(fp);
ActRec* sfp = fp->arGetSfp();
if (sfp == fp) {
// In the reentrant case, we can consult the savedVM state. We simply
// use the top of stack of the previous VM frame (since the ActRec,
@@ -1180,16 +1190,6 @@ __thread VarEnvArenaStorage s_varEnvArenaStorage;
using namespace HPHP::VM;
using namespace HPHP::MethodLookup;
ActRec* VMExecutionContext::arGetSfp(const ActRec* ar) {
ActRec* prevFrame = (ActRec*)ar->m_savedRbp;
if (LIKELY(((uintptr_t)prevFrame - Util::s_stackLimit) >=
Util::s_stackSize)) {
if (LIKELY(prevFrame != nullptr)) return prevFrame;
}
return const_cast<ActRec*>(ar);
}
ActRec* VMExecutionContext::getOuterVMFrame(const ActRec* ar) {
ActRec* prevFrame = (ActRec*)ar->m_savedRbp;
if (LIKELY(((uintptr_t)prevFrame - Util::s_stackLimit) >=
@@ -1698,65 +1698,62 @@ static inline void checkStack(Stack& stk, const Func* f) {
}
}
template <bool reenter>
bool VMExecutionContext::prepareFuncEntry(ActRec *ar,
PC& pc,
ExtraArgs* extraArgs) {
bool VMExecutionContext::prepareFuncEntry(ActRec *ar, PC& pc) {
const Func* func = ar->m_func;
if (!reenter) {
// For the reenter case, magic shuffling are handled
// in invokeFunc() before calling prepareFuncEntry().
if (UNLIKELY(ar->hasInvName())) {
shuffleMagicArgs(ar);
}
}
// It is now safe to access m_varEnv directly
assert(!ar->hasInvName());
int nargs = ar->numArgs();
// Set pc below, once we know that DV dispatch is unnecessary.
m_fp = ar;
Offset firstDVInitializer = InvalidAbsoluteOffset;
bool raiseMissingArgumentWarnings = false;
int nparams = func->numParams();
Offset firstDVInitializer = InvalidAbsoluteOffset;
if (nargs != nparams) {
if (nargs < nparams) {
// Push uninitialized nulls for missing arguments. Some of them may end
// up getting default-initialized, but regardless, we need to make space
// for them on the stack.
const Func::ParamInfoVec& paramInfo = func->params();
for (int i = nargs; i < nparams; ++i) {
m_stack.pushUninit();
Offset dvInitializer = paramInfo[i].funcletOff();
if (dvInitializer == InvalidAbsoluteOffset) {
// We wait to raise warnings until after all the locals have been
// initialized. This is important because things need to be in a
// consistent state in case the user error handler throws.
raiseMissingArgumentWarnings = true;
} else if (firstDVInitializer == InvalidAbsoluteOffset) {
// This is the first unpassed arg with a default value, so
// this is where we'll need to jump to.
firstDVInitializer = dvInitializer;
}
if (UNLIKELY(ar->m_varEnv != nullptr)) {
/*
* m_varEnv != nullptr => we have a varEnv, extraArgs, or an invName.
*/
if (ar->hasInvName()) {
// shuffleMagicArgs deals with everything. no need for
// further argument munging
shuffleMagicArgs(ar);
} else if (ar->hasVarEnv()) {
m_fp = ar;
if (!func->isGenerator()) {
assert(func->isPseudoMain());
pushLocalsAndIterators(func);
ar->m_varEnv->attach(ar);
}
assert(m_fp->m_func == func);
pc = func->getEntry();
// Nothing more to do; get out
return true;
} else {
// For the reenter case, extra arguments are handled below (with
// the extraArgs vector passed to this function). The below
// handles pulling extra args from the execution stack in a
// non-reentry case.
if (!reenter) {
assert(ar->hasExtraArgs());
assert(func->numParams() < ar->numArgs());
}
} else {
int nargs = ar->numArgs();
if (nargs != nparams) {
if (nargs < nparams) {
// Push uninitialized nulls for missing arguments. Some of them may end
// up getting default-initialized, but regardless, we need to make space
// for them on the stack.
const Func::ParamInfoVec& paramInfo = func->params();
for (int i = nargs; i < nparams; ++i) {
m_stack.pushUninit();
Offset dvInitializer = paramInfo[i].funcletOff();
if (dvInitializer == InvalidAbsoluteOffset) {
// We wait to raise warnings until after all the locals have been
// initialized. This is important because things need to be in a
// consistent state in case the user error handler throws.
raiseMissingArgumentWarnings = true;
} else if (firstDVInitializer == InvalidAbsoluteOffset) {
// This is the first unpassed arg with a default value, so
// this is where we'll need to jump to.
firstDVInitializer = dvInitializer;
}
}
} else {
if (func->attrs() & AttrMayUseVV) {
// If there are extra parameters then we cannot be a pseudomain
// inheriting a VarEnv
assert(!m_fp->m_varEnv);
// Extra parameters must be moved off the stack.
const int numExtras = nargs - nparams;
m_fp->setExtraArgs(ExtraArgs::allocateCopy(
(TypedValue*)(uintptr_t(m_fp) - nargs * sizeof(TypedValue)),
numExtras));
for (int i = 0; i < numExtras; i++) {
m_stack.discard();
}
ar->setExtraArgs(ExtraArgs::allocateCopy((TypedValue*)ar - nargs,
numExtras));
m_stack.ndiscard(numExtras);
} else {
// The function we're calling is not marked as "MayUseVV",
// so just discard the extra arguments
@@ -1788,37 +1785,20 @@ bool VMExecutionContext::prepareFuncEntry(ActRec *ar,
pushLocalsAndIterators(func, nlocals);
}
/*
* If we're reentering, make sure to finalize the ActRec before
* possibly raising any exceptions, so unwinding won't get confused.
*/
if (reenter) {
if (ar->hasVarEnv()) {
// If this is a pseudomain inheriting a VarEnv from our caller,
// there cannot be extra arguments
assert(!extraArgs);
// Now that locals have been initialized, it is safe to attach
// the VarEnv inherited from our caller to the current frame
ar->m_varEnv->attach(ar);
} else if (extraArgs) {
// Create an ExtraArgs structure and stash the extra args in
// there.
ar->setExtraArgs(extraArgs);
}
m_fp = ar;
if (firstDVInitializer != InvalidAbsoluteOffset) {
pc = func->unit()->entry() + firstDVInitializer;
} else {
pc = func->getEntry();
}
// cppext functions/methods have their own logic for raising
// warnings for missing arguments, so we only need to do this work
// for non-cppext functions/methods
if (raiseMissingArgumentWarnings && !func->isBuiltin()) {
pc = func->getEntry();
// m_pc is not set to callee. if the callee is in a different unit,
// debugBacktrace() can barf in unit->offsetOf(m_pc) where it
// asserts that m_pc >= m_bc && m_pc < m_bc + m_bclen. Sync m_fp
// to function entry point in called unit.
// need to sync m_pc to pc for backtraces/re-entry
SYNC();
const Func::ParamInfoVec& paramInfo = func->params();
for (int i = nargs; i < nparams; ++i) {
for (int i = ar->numArgs(); i < nparams; ++i) {
Offset dvInitializer = paramInfo[i].funcletOff();
if (dvInitializer == InvalidAbsoluteOffset) {
const char* name = func->name()->data();
@@ -1830,12 +1810,6 @@ bool VMExecutionContext::prepareFuncEntry(ActRec *ar,
}
}
}
if (firstDVInitializer != InvalidAbsoluteOffset) {
pc = func->unit()->entry() + firstDVInitializer;
} else {
pc = func->getEntry();
}
return true;
}
@@ -1845,17 +1819,39 @@ void VMExecutionContext::syncGdbState() {
}
}
void VMExecutionContext::enterVMPrologue(ActRec* enterFnAr) {
assert(enterFnAr);
Stats::inc(Stats::VMEnter);
if (ThreadInfo::s_threadInfo->m_reqInjectionData.getJit()) {
int np = enterFnAr->m_func->numParams();
int na = enterFnAr->numArgs();
if (na > np) na = np + 1;
TCA start = enterFnAr->m_func->getPrologue(na);
tx64->enterTCAtProlog(enterFnAr, start);
} else {
if (prepareFuncEntry(enterFnAr, m_pc)) {
enterVMWork(enterFnAr);
}
}
}
void VMExecutionContext::enterVMWork(ActRec* enterFnAr) {
TCA start = nullptr;
if (enterFnAr) {
if (!EventHook::FunctionEnter(enterFnAr, EventHook::NormalFunc)) return;
checkStack(m_stack, enterFnAr->m_func);
start = enterFnAr->m_func->getFuncBody();
}
Stats::inc(Stats::VMEnter);
if (LIKELY(ThreadInfo::s_threadInfo->m_reqInjectionData.getJit())) {
Transl::SrcKey sk(curFunc(), m_pc);
if (ThreadInfo::s_threadInfo->m_reqInjectionData.getJit()) {
(void) curUnit()->offsetOf(m_pc); /* assert */
tx64->enterTC(sk, start);
if (enterFnAr) {
assert(start);
tx64->enterTCAfterProlog(start);
} else {
Transl::SrcKey sk(curFunc(), m_pc);
tx64->enterTCAtSrcKey(sk);
}
} else {
dispatch();
}
@@ -1912,9 +1908,7 @@ static int exception_handler() {
return longJmpType;
}
void VMExecutionContext::enterVM(TypedValue* retval,
ActRec* ar,
ExtraArgs* extraArgs) {
void VMExecutionContext::enterVM(TypedValue* retval, ActRec* ar) {
m_firstAR = ar;
ar->m_savedRip = (uintptr_t)tx64->getCallToExit();
assert(isReturnHelper(ar->m_savedRip));
@@ -1945,8 +1939,12 @@ short_jump:
try {
switch (jumpCode) {
case EXCEPTION_START:
if (prepareFuncEntry<true>(ar, m_pc, extraArgs)) {
enterVMWork(ar);
if (m_fp && !ar->m_varEnv) {
enterVMPrologue(ar);
} else {
if (prepareFuncEntry(ar, m_pc)) {
enterVMWork(ar);
}
}
break;
case EXCEPTION_PROPAGATE:
@@ -2007,7 +2005,6 @@ propagate:
void VMExecutionContext::reenterVM(TypedValue* retval,
ActRec* ar,
ExtraArgs* extraArgs,
TypedValue* savedSP) {
ar->m_soff = 0;
ar->m_savedRbp = 0;
@@ -2016,7 +2013,7 @@ void VMExecutionContext::reenterVM(TypedValue* retval,
pushVMState(savedVM, ar);
assert(m_nestedVMs.size() >= 1);
try {
enterVM(retval, ar, extraArgs);
enterVM(retval, ar);
popVMState();
} catch (...) {
popVMState();
@@ -2069,7 +2066,9 @@ void VMExecutionContext::invokeFunc(TypedValue* retval,
}
Cell* savedSP = m_stack.top();
checkStack(m_stack, f);
if (f->numParams() > kStackCheckReenterPadding - kNumActRecCells) {
checkStack(m_stack, f);
}
if (flags & InvokePseudoMain) {
assert(f->isPseudoMain() && !params.get());
@@ -2111,7 +2110,6 @@ void VMExecutionContext::invokeFunc(TypedValue* retval,
#endif
ArrayData *arr = params.get();
ExtraArgs* extraArgs = nullptr;
if (isMagicCall) {
// Put the method name into the location of the first parameter. We
// are transferring ownership, so no need to incRef/decRef here.
@@ -2121,8 +2119,10 @@ void VMExecutionContext::invokeFunc(TypedValue* retval,
} else if (arr) {
const int numParams = f->numParams();
const int numExtraArgs = arr->size() - numParams;
ExtraArgs* extraArgs = nullptr;
if (numExtraArgs > 0 && (f->attrs() & AttrMayUseVV)) {
extraArgs = ExtraArgs::allocateUninit(numExtraArgs);
ar->setExtraArgs(extraArgs);
}
int paramId = 0;
for (ssize_t i = arr->iter_begin();
@@ -2160,6 +2160,7 @@ void VMExecutionContext::invokeFunc(TypedValue* retval,
if (extraArgs) {
int n = paramId >= numParams ? paramId - numParams + 1 : 0;
ExtraArgs::deallocate(extraArgs, n);
ar->m_varEnv = nullptr;
paramId -= n;
}
while (paramId >= 0) {
@@ -2175,10 +2176,73 @@ void VMExecutionContext::invokeFunc(TypedValue* retval,
}
if (m_fp) {
reenterVM(retval, ar, extraArgs, savedSP);
reenterVM(retval, ar, savedSP);
} else {
assert(m_nestedVMs.size() == 0);
enterVM(retval, ar, extraArgs);
enterVM(retval, ar);
}
}
void VMExecutionContext::invokeFuncFew(TypedValue* retval,
const Func* f,
void* thisOrCls,
StringData* invName,
int argc, TypedValue* argv) {
assert(retval);
assert(f);
// If this is a regular function, this_ and cls must be NULL
assert(f->preClass() || !thisOrCls);
// If this is a method, either this_ or cls must be non-NULL
assert(!f->preClass() || thisOrCls);
// If this is a static method, this_ must be NULL
assert(!(f->attrs() & AttrStatic && !f->isClosureBody()) ||
!ActRec::decodeThis(thisOrCls));
// invName should only be non-NULL if we are calling __call or
// __callStatic
assert(!invName || f->name()->isame(s___call.get()) ||
f->name()->isame(s___callStatic.get()));
VMRegAnchor _;
if (ObjectData* thiz = ActRec::decodeThis(thisOrCls)) {
thiz->incRefCount();
}
Cell* savedSP = m_stack.top();
if (argc > kStackCheckReenterPadding - kNumActRecCells) {
checkStack(m_stack, f);
}
ActRec* ar = m_stack.allocA();
ar->m_soff = 0;
ar->m_savedRbp = 0;
ar->m_func = f;
ar->m_this = (ObjectData*)thisOrCls;
ar->initNumArgs(argc);
if (UNLIKELY(invName != nullptr)) {
ar->setInvName(invName);
} else {
ar->m_varEnv = nullptr;
}
#ifdef HPHP_TRACE
if (m_fp == nullptr) {
TRACE(1, "Reentry: enter %s(%p) from top-level\n",
f->name()->data(), ar);
} else {
TRACE(1, "Reentry: enter %s(pc %p ar %p) from %s(%p)\n",
f->name()->data(), m_pc, ar,
m_fp->m_func ? m_fp->m_func->name()->data() : "unknownBuiltin", m_fp);
}
#endif
for (int i = 0; i < argc; i++) {
*m_stack.allocTV() = *argv++;
}
if (m_fp) {
reenterVM(retval, ar, savedSP);
} else {
assert(m_nestedVMs.size() == 0);
enterVM(retval, ar);
}
}
@@ -2193,7 +2257,9 @@ void VMExecutionContext::invokeContFunc(const Func* f,
this_->incRefCount();
Cell* savedSP = m_stack.top();
checkStack(m_stack, f);
// no need to check stack due to ReenterPadding
assert(kStackCheckReenterPadding - kNumActRecCells >= 1);
ActRec* ar = m_stack.allocA();
ar->m_savedRbp = 0;
@@ -2208,7 +2274,7 @@ void VMExecutionContext::invokeContFunc(const Func* f,
}
TypedValue retval;
reenterVM(&retval, ar, nullptr, savedSP);
reenterVM(&retval, ar, savedSP);
// Codegen for generator functions guarantees that they will return null
assert(IS_NULL_TYPE(retval.m_type));
}
@@ -2266,7 +2332,7 @@ ActRec* VMExecutionContext::getPrevVMState(const ActRec* fp,
if (fp == nullptr) {
return nullptr;
}
ActRec* prevFp = arGetSfp(fp);
ActRec* prevFp = fp->arGetSfp();
if (prevFp != fp) {
if (prevSp) {
if (UNLIKELY(fp->m_func->isGenerator())) {
@@ -4532,7 +4598,7 @@ inline void OPTBLD_INLINE VMExecutionContext::iopRetC(PC& pc) {
// Call the runtime helpers to free the local variables and iterators
frame_free_locals_inl(m_fp, m_fp->m_func->numLocals());
ActRec* sfp = arGetSfp(m_fp);
ActRec* sfp = m_fp->arGetSfp();
// Memcpy the the return value on top of the activation record. This works
// the same regardless of whether the return value is boxed or not.
TypedValue* retval_ptr = &m_fp->m_r;
@@ -5982,7 +6048,7 @@ void VMExecutionContext::iopFPassM(PC& pc) {
}
void VMExecutionContext::doFCall(ActRec* ar, PC& pc) {
assert(ar->m_savedRbp == (uint64_t)m_fp);
assert(getOuterVMFrame(ar) == m_fp);
ar->m_savedRip = (uintptr_t)tx64->getRetFromInterpretedFrame();
assert(isReturnHelper(ar->m_savedRip));
TRACE(3, "FCall: pc %p func %p base %d\n", m_pc,
@@ -5990,8 +6056,8 @@ void VMExecutionContext::doFCall(ActRec* ar, PC& pc) {
int(m_fp->m_func->base()));
ar->m_soff = m_fp->m_func->unit()->offsetOf(pc)
- (uintptr_t)m_fp->m_func->base();
assert(pcOff() > m_fp->m_func->base());
prepareFuncEntry<false>(ar, pc, 0);
assert(pcOff() >= m_fp->m_func->base());
prepareFuncEntry(ar, pc);
SYNC();
if (!EventHook::FunctionEnter(ar, EventHook::NormalFunc)) {
pc = m_pc;
@@ -6146,81 +6212,82 @@ inline void OPTBLD_INLINE VMExecutionContext::iopFCallBuiltin(PC& pc) {
}
bool VMExecutionContext::prepareArrayArgs(ActRec* ar,
ArrayData* args,
ExtraArgs*& extraArgs) {
extraArgs = nullptr;
ArrayData* args) {
if (UNLIKELY(ar->hasInvName())) {
m_stack.pushStringNoRc(ar->getInvName());
m_stack.pushArray(args);
ar->setVarEnv(0);
ar->initNumArgs(2);
} else {
int nargs = args->size();
const Func* f = ar->m_func;
int nparams = f->numParams();
int extra = nargs - nparams;
if (extra < 0) {
extra = 0;
nparams = nargs;
return true;
}
int nargs = args->size();
const Func* f = ar->m_func;
int nparams = f->numParams();
int extra = nargs - nparams;
if (extra < 0) {
extra = 0;
nparams = nargs;
}
ssize_t pos = args->iter_begin();
for (int i = 0; i < nparams; ++i) {
TypedValue* from = const_cast<TypedValue*>(
args->getValueRef(pos).asTypedValue());
TypedValue* to = m_stack.allocTV();
if (UNLIKELY(f->byRef(i))) {
if (UNLIKELY(!tvAsVariant(from).isReferenced())) {
raise_warning("Parameter %d to %s() expected to be a reference, "
"value given", i + 1, f->fullName()->data());
if (skipCufOnInvalidParams) {
m_stack.discard();
while (i--) m_stack.popTV();
m_stack.popAR();
m_stack.pushNull();
return false;
}
}
tvDup(from, to);
} else {
tvDup(from, to);
if (UNLIKELY(to->m_type == KindOfRef)) {
tvUnbox(to);
}
}
ssize_t pos = args->iter_begin();
for (int i = 0; i < nparams; ++i) {
TypedValue* from = const_cast<TypedValue*>(
args->getValueRef(pos).asTypedValue());
TypedValue* to = m_stack.allocTV();
if (UNLIKELY(f->byRef(i))) {
if (UNLIKELY(!tvAsVariant(from).isReferenced())) {
raise_warning("Parameter %d to %s() expected to be a reference, "
"value given", i + 1, f->fullName()->data());
if (skipCufOnInvalidParams) {
m_stack.discard();
while (i--) m_stack.popTV();
m_stack.popAR();
m_stack.pushNull();
return false;
}
}
tvDup(from, to);
} else {
tvDup(from, to);
if (UNLIKELY(to->m_type == KindOfRef)) {
tvUnbox(to);
}
pos = args->iter_advance(pos);
}
if (extra && (ar->m_func->attrs() & AttrMayUseVV)) {
ExtraArgs* extraArgs = ExtraArgs::allocateUninit(extra);
for (int i = 0; i < extra; ++i) {
TypedValue* to = extraArgs->getExtraArg(i);
tvDup(args->getValueRef(pos).asTypedValue(), to);
if (to->m_type == KindOfRef && to->m_data.pref->_count == 2) {
tvUnbox(to);
}
pos = args->iter_advance(pos);
}
if (extra && (ar->m_func->attrs() & AttrMayUseVV)) {
extraArgs = ExtraArgs::allocateUninit(extra);
for (int i = 0; i < extra; ++i) {
TypedValue* to = extraArgs->getExtraArg(i);
tvDup(args->getValueRef(pos).asTypedValue(), to);
if (to->m_type == KindOfRef && to->m_data.pref->_count == 2) {
tvUnbox(to);
}
pos = args->iter_advance(pos);
}
ar->initNumArgs(nargs);
} else {
ar->initNumArgs(nparams);
}
ar->setExtraArgs(extraArgs);
ar->initNumArgs(nargs);
} else {
ar->initNumArgs(nparams);
}
return true;
}
static void cleanupParamsAndActRec(Stack& stack,
ActRec* ar,
ExtraArgs* extraArgs) {
ActRec* ar,
ExtraArgs* extraArgs) {
assert(stack.top() + (extraArgs ?
ar->m_func->numParams() :
ar->numArgs()) == (void*)ar);
while (stack.top() != (void*)ar) {
stack.popTV();
}
stack.popAR();
if (extraArgs) {
const int numExtra = ar->numArgs() - ar->m_func->numParams();
ExtraArgs::deallocate(extraArgs, numExtra);
}
while (stack.top() != (void*)ar) {
stack.popTV();
}
stack.popAR();
}
bool VMExecutionContext::doFCallArray(PC& pc) {
@@ -6240,7 +6307,6 @@ bool VMExecutionContext::doFCallArray(PC& pc) {
}
const Func* func = ar->m_func;
ExtraArgs* extraArgs = nullptr;
{
Array args(LIKELY(c1->m_type == KindOfArray) ? c1->m_data.parr :
tvAsVariant(c1).toArray().get());
@@ -6258,10 +6324,10 @@ bool VMExecutionContext::doFCallArray(PC& pc) {
- (uintptr_t)m_fp->m_func->base();
assert(pcOff() > m_fp->m_func->base());
if (UNLIKELY(!prepareArrayArgs(ar, args.get(), extraArgs))) return false;
if (UNLIKELY(!prepareArrayArgs(ar, args.get()))) return false;
}
if (UNLIKELY(!(prepareFuncEntry<true>(ar, pc, extraArgs)))) {
if (UNLIKELY(!(prepareFuncEntry(ar, pc)))) {
return false;
}
SYNC();
@@ -6694,7 +6760,7 @@ inline void OPTBLD_INLINE VMExecutionContext::iopNativeImpl(PC& pc) {
// Adjust the stack; the native implementation put the return value in the
// right place for us already
m_stack.ndiscard(m_fp->m_func->numSlotsInFrame());
ActRec* sfp = arGetSfp(m_fp);
ActRec* sfp = m_fp->arGetSfp();
if (LIKELY(sfp != m_fp)) {
// Restore caller's execution state.
m_fp = sfp;
@@ -6969,7 +7035,7 @@ void VMExecutionContext::iopContExit(PC& pc) {
NEXT();
EventHook::FunctionExit(m_fp);
ActRec* prevFp = arGetSfp(m_fp);
ActRec* prevFp = m_fp->arGetSfp();
pc = prevFp->m_func->getEntry() + m_fp->m_soff;
m_fp = prevFp;
}
@@ -7029,7 +7095,7 @@ inline void OPTBLD_INLINE VMExecutionContext::iopContRetC(PC& pc) {
m_stack.popC();
EventHook::FunctionExit(m_fp);
ActRec* prevFp = arGetSfp(m_fp);
ActRec* prevFp = m_fp->arGetSfp();
pc = prevFp->m_func->getEntry() + m_fp->m_soff;
m_fp = prevFp;
}
@@ -7428,16 +7494,13 @@ void VMExecutionContext::pushVMState(VMState &savedVM,
void VMExecutionContext::popVMState() {
assert(m_nestedVMs.size() >= 1);
VMState savedVM;
memcpy(&savedVM, &m_nestedVMs.back(), sizeof(savedVM));
VMState &savedVM = m_nestedVMs.back().m_savedState;
m_pc = savedVM.pc;
m_fp = savedVM.fp;
m_firstAR = savedVM.firstAR;
assert(m_stack.top() == savedVM.sp);
if (debug) {
const ReentryRecord& rr = m_nestedVMs.back();
const VMState& savedVM = rr.m_savedState;
if (savedVM.fp &&
savedVM.fp->m_func &&
savedVM.fp->m_func->unit()) {
+27 -2
Ver Arquivo
@@ -256,6 +256,10 @@ struct ActRec {
};
};
// Get the next outermost VM frame, but if this is
// a re-entry frame, return ar
ActRec* arGetSfp() const;
// skip this frame if it is for a builtin function
bool skipFrame() const;
@@ -288,6 +292,22 @@ struct ActRec {
initNumArgs(numArgs, isFromFPushCtor());
}
static void* encodeThis(ObjectData* obj, VM::Class* cls) {
if (obj) return obj;
if (cls) return (char*)cls + 1;
}
static void* encodeThis(ObjectData* obj) { return obj; }
static void* encodeClass(const VM::Class* cls) {
return cls ? (char*)cls + 1 : nullptr;
}
static ObjectData* decodeThis(void* p) {
return uintptr_t(p) & 1 ? nullptr : (ObjectData*)p;
}
static VM::Class* decodeClass(void* p) {
return uintptr_t(p) & 1 ? (VM::Class*)(uintptr_t(p)&~1LL) : nullptr;
}
/**
* To conserve space, we use unions for pairs of mutually exclusive
* fields (fields that are not used at the same time). We use unions
@@ -419,15 +439,20 @@ constexpr size_t kNumIterCells = sizeof(Iter) / sizeof(Cell);
constexpr size_t kNumActRecCells = sizeof(ActRec) / sizeof(Cell);
/*
* We pad all stack overflow checks by a small amount to allow for two
* We pad all stack overflow checks by a small amount to allow for three
* things:
*
* - inlining functions without having to either do another stack
* check (or chase down prologues to smash checks to be bigger).
*
* - omitting stack overflow checks on leaf functions
*
* - delaying stack overflow checks on reentry
*/
constexpr int kStackCheckPadding = 20;
constexpr int kStackCheckLeafPadding = 20;
constexpr int kStackCheckReenterPadding = 9;
constexpr int kStackCheckPadding = kStackCheckLeafPadding +
kStackCheckReenterPadding;
struct Fault {
enum Type : int16_t {
+6 -6
Ver Arquivo
@@ -1200,12 +1200,12 @@ TypedValue* Class::clsCnsGet(const StringData* clsCnsName) const {
static StringData* sd86cinit = StringData::GetStaticString("86cinit");
const Func* meth86cinit =
m_constants[clsCnsInd].m_class->lookupMethod(sd86cinit);
TypedValue tv;
tv.m_data.pstr = (StringData*)clsCnsName;
tv.m_type = KindOfString;
g_vmContext->invokeFunc(clsCns, meth86cinit,
CREATE_VECTOR1(tvAsCVarRef(&tv)), nullptr,
const_cast<Class*>(this));
TypedValue tv[1];
tv->m_data.pstr = (StringData*)clsCnsName;
tv->m_type = KindOfString;
clsCnsName->incRefCount();
g_vmContext->invokeFuncFew(clsCns, meth86cinit, ActRec::encodeClass(this),
nullptr, 1, tv);
}
return clsCns;
}
+22 -16
Ver Arquivo
@@ -76,7 +76,7 @@ Instance* Instance::callCustomInstanceInit() {
// reasonable refcount.
try {
incRefCount();
g_vmContext->invokeFunc(&tv, init, null_array, this);
g_vmContext->invokeFuncFew(&tv, init, this);
decRefCount();
assert(!IS_REFCOUNTED_TYPE(tv.m_type));
} catch (...) {
@@ -625,7 +625,7 @@ Variant Instance::t___destruct() {
const Func* method = m_cls->lookupMethod(sd__destruct);
if (method) {
Variant v;
g_vmContext->invokeFunc((TypedValue*)&v, method, null_array, this);
g_vmContext->invokeFuncFew((TypedValue*)&v, method, this);
return v;
} else {
return uninit_null();
@@ -637,8 +637,10 @@ Variant Instance::t___call(Variant v_name, Variant v_arguments) {
const Func* method = m_cls->lookupMethod(sd__call);
if (method) {
Variant v;
g_vmContext->invokeFunc((TypedValue*)&v, method,
CREATE_VECTOR2(v_name, v_arguments), this);
TypedValue args[2];
tvDup(v_name.asTypedValue(), args + 0);
tvDup(v_arguments.asTypedValue(), args + 1);
g_vmContext->invokeFuncFew((TypedValue*)&v, method, this, nullptr, 2, args);
return v;
} else {
return uninit_null();
@@ -662,8 +664,9 @@ Variant Instance::t___get(Variant v_name) {
const Func* method = m_cls->lookupMethod(s___get.get());
if (method) {
Variant v;
g_vmContext->invokeFunc((TypedValue*)&v, method,
CREATE_VECTOR1(v_name), this);
TypedValue args[1];
tvDup(v_name.asTypedValue(), args + 0);
g_vmContext->invokeFuncFew((TypedValue*)&v, method, this, nullptr, 1, args);
return v;
} else {
return uninit_null();
@@ -674,8 +677,9 @@ bool Instance::t___isset(Variant v_name) {
const Func* method = m_cls->lookupMethod(s___isset.get());
if (method) {
Variant v;
g_vmContext->invokeFunc((TypedValue*)&v, method,
CREATE_VECTOR1(v_name), this);
TypedValue args[1];
tvDup(v_name.asTypedValue(), args + 0);
g_vmContext->invokeFuncFew((TypedValue*)&v, method, this, nullptr, 1, args);
return v;
} else {
return uninit_null();
@@ -686,8 +690,9 @@ Variant Instance::t___unset(Variant v_name) {
const Func* method = m_cls->lookupMethod(s___unset.get());
if (method) {
Variant v;
g_vmContext->invokeFunc((TypedValue*)&v, method,
CREATE_VECTOR1(v_name), this);
TypedValue args[1];
tvDup(v_name.asTypedValue(), args + 0);
g_vmContext->invokeFuncFew((TypedValue*)&v, method, this, nullptr, 1, args);
return v;
} else {
return uninit_null();
@@ -699,7 +704,7 @@ Variant Instance::t___sleep() {
const Func *method = m_cls->lookupMethod(sd__sleep);
if (method) {
TypedValue tv;
g_vmContext->invokeFunc(&tv, method, null_array, this);
g_vmContext->invokeFuncFew(&tv, method, this);
return tvAsVariant(&tv);
} else {
clearAttribute(HasSleep);
@@ -712,7 +717,7 @@ Variant Instance::t___wakeup() {
const Func *method = m_cls->lookupMethod(sd__wakeup);
if (method) {
TypedValue tv;
g_vmContext->invokeFunc(&tv, method, null_array, this);
g_vmContext->invokeFuncFew(&tv, method, this);
return tvAsVariant(&tv);
} else {
return uninit_null();
@@ -724,8 +729,9 @@ Variant Instance::t___set_state(Variant v_properties) {
const Func* method = m_cls->lookupMethod(sd__set_state);
if (method) {
Variant v;
g_vmContext->invokeFunc((TypedValue*)&v, method,
CREATE_VECTOR1(v_properties), this);
TypedValue args[1];
tvDup(v_properties.asTypedValue(), args + 0);
g_vmContext->invokeFuncFew((TypedValue*)&v, method, this, nullptr, 1, args);
return v;
} else {
return false;
@@ -736,7 +742,7 @@ String Instance::t___tostring() {
const Func *method = m_cls->getToString();
if (method) {
TypedValue tv;
g_vmContext->invokeFunc(&tv, method, null_array, this);
g_vmContext->invokeFuncFew(&tv, method, this);
if (!IS_STRING_TYPE(tv.m_type)) {
void (*notify_user)(const char *, ...) = &raise_error;
if (hphpiCompat) {
@@ -759,7 +765,7 @@ Variant Instance::t___clone() {
const Func *method = m_cls->lookupMethod(sd__clone);
if (method) {
TypedValue tv;
g_vmContext->invokeFunc(&tv, method, null_array, this);
g_vmContext->invokeFuncFew(&tv, method, this);
return false;
} else {
return false;
@@ -1127,8 +1127,8 @@ static bool shouldIRInline(const Func* curFunc,
if (func->numIterators() != 0) {
return refuse("iterators");
}
if (func->maxStackCells() >= kStackCheckPadding) {
FTRACE(1, "{} >= {}\n", func->maxStackCells(), kStackCheckPadding);
if (func->maxStackCells() >= kStackCheckLeafPadding) {
FTRACE(1, "{} >= {}\n", func->maxStackCells(), kStackCheckLeafPadding);
return refuse("too many stack cells");
}
+1 -1
Ver Arquivo
@@ -98,7 +98,7 @@ struct VMRegAnchor : private boost::noncopyable {
m_old = REGSTATE_DIRTY;
tl_regState = REGSTATE_CLEAN;
auto prevAr = (ActRec*)ar->m_savedRbp;
auto prevAr = g_vmContext->getOuterVMFrame(ar);
const Func* prevF = prevAr->m_func;
vmsp() = ar->m_func->isGenerator() ?
Stack::generatorStackBase(ar) :
+27 -17
Ver Arquivo
@@ -1652,13 +1652,12 @@ TranslatorX64::shuffleArgsForMagicCall(ActRec* ar) {
*/
static void sync_regstate_to_caller(ActRec* preLive) {
assert(tl_regState == REGSTATE_DIRTY);
vmfp() = (TypedValue*)preLive->m_savedRbp;
vmsp() = (TypedValue*)preLive - preLive->numArgs();
if (ActRec* fp = g_vmContext->m_fp) {
if (fp->m_func && fp->m_func->unit()) {
vmpc() = fp->m_func->unit()->at(fp->m_func->base() + preLive->m_soff);
}
}
VMExecutionContext* ec = g_vmContext;
ec->m_stack.top() = (TypedValue*)preLive - preLive->numArgs();
ActRec* fp = preLive == ec->m_firstAR ?
ec->m_nestedVMs.back().m_savedState.fp : (ActRec*)preLive->m_savedRbp;
ec->m_fp = fp;
ec->m_pc = fp->m_func->unit()->at(fp->m_func->base() + preLive->m_soff);
tl_regState = REGSTATE_CLEAN;
}
@@ -2032,11 +2031,11 @@ TranslatorX64::funcPrologue(Func* func, int nPassed, ActRec* ar) {
/*
* Guard: we have stack enough stack space to complete this
* function. We omit overflow checks if it is a leaf function
* that can't use more than kStackCheckPadding cells.
* that can't use more than kStackCheckLeafPadding cells.
*/
auto const needStackCheck =
!(func->attrs() & AttrPhpLeafFn) ||
func->maxStackCells() >= kStackCheckPadding;
func->maxStackCells() >= kStackCheckLeafPadding;
if (needStackCheck) {
emitStackCheck(cellsToBytes(func->maxStackCells()), func->base());
}
@@ -2238,7 +2237,7 @@ TranslatorX64::emitPrologue(Func* func, int nPassed) {
a.xorl (eax, eax);
}
for (k = numLocals; k < func->numLocals(); ++k) {
locToRegDisp(Location(Location::Local, k), &base, &disp);
locToRegDisp(Location(Location::Local, k), &base, &disp, func);
emitStoreTVType(a, eax, base[disp + TVOFF(m_type)]);
}
}
@@ -2944,7 +2943,7 @@ struct TReqInfo {
void
TranslatorX64::enterTC(SrcKey sk, TCA start) {
TranslatorX64::enterTC(TCA start, void* data) {
using namespace TargetCache;
if (debug) {
@@ -2953,9 +2952,17 @@ TranslatorX64::enterTC(SrcKey sk, TCA start) {
}
DepthGuard d;
TReqInfo info;
info.requestNum = -1;
info.saved_rStashedAr = 0;
if (UNLIKELY(!start)) start = getTranslation(sk, true);
SrcKey sk;
if (LIKELY(start != nullptr)) {
info.requestNum = data ? REQ_BIND_CALL : -1;
info.saved_rStashedAr = (uintptr_t)data;
} else {
info.requestNum = -1;
info.saved_rStashedAr = 0;
sk = *(SrcKey*)data;
start = getTranslation(sk, true);
}
for (;;) {
assert(sizeof(Cell) == 16);
assert(((uintptr_t)vmsp() & (sizeof(Cell) - 1)) == 0);
@@ -2975,13 +2982,16 @@ TranslatorX64::enterTC(SrcKey sk, TCA start) {
start = getTranslation(sk, true);
}
assert(start == (TCA)HPHP::VM::Transl::funcBodyHelperThunk ||
isValidCodeAddress(start));
isValidCodeAddress(start) ||
(start == (TCA)HPHP::VM::Transl::fcallHelperThunk &&
info.saved_rStashedAr == (uintptr_t)data));
assert(!s_writeLease.amOwner());
curFunc()->validate();
const Func* func = (vmfp() ? (ActRec*)vmfp() : (ActRec*)data)->m_func;
func->validate();
INC_TPC(enter_tc);
TRACE(1, "enterTC: %p fp%p(%s) sp%p enter {\n", start,
vmfp(), ((ActRec*)vmfp())->m_func->name()->data(), vmsp());
vmfp(), func->name()->data(), vmsp());
tl_regState = REGSTATE_DIRTY;
// We have to force C++ to spill anything that might be in a callee-saved
+17 -1
Ver Arquivo
@@ -1003,8 +1003,24 @@ public:
* bytecode interpreter (see enterVMWork). It operates on behalf of
* a given nested invocation of the intepreter (calling back into it
* as necessary for blocks that need to be interpreted).
*
* If start is not null, data will be used to initialize rStashedAr,
* to enable us to run a jitted prolog;
* otherwise, data should be a pointer to the SrcKey to start
* translating from.
*
* But don't call this directly, use one of the helpers below
*/
void enterTC(SrcKey sk, TCA start);
void enterTC(TCA start, void* data);
void enterTCAtSrcKey(SrcKey& sk) {
enterTC(nullptr, &sk);
}
void enterTCAtProlog(ActRec *ar, TCA start) {
enterTC(start, ar);
}
void enterTCAfterProlog(TCA start) {
enterTC(start, nullptr);
}
TranslatorX64();
virtual ~TranslatorX64();
+1 -1
Ver Arquivo
@@ -1 +1 @@
HipHop Fatal error: Stack overflow in %s on line 21
HipHop Fatal error: Stack overflow in %s on line 31