Rework constants to use the target cache exclusively

Make the targetcache the one true home for constants,
so we dont need to (also) insert them all into
VMExecutionContext::m_constants (which is now gone).

By also making "non-volatile" constants persistent, we save
initializing most of them at all in RepoAuthoritative mode.
Esse commit está contido em:
mwilliams
2013-03-12 11:03:10 -07:00
commit de Sara Golemon
commit 5f9df0e7c6
32 arquivos alterados com 436 adições e 367 exclusões
+3 -1
Ver Arquivo
@@ -1677,7 +1677,9 @@ void EmitterVisitor::visit(FileScopePtr file) {
StringData *name;
TypedValue tv;
if (func->isSimpleDefine(&name, &tv)) {
m_ue.pushMergeableDef(UnitMergeKindDefine, name, tv);
UnitMergeKind k = func->isDefineWithoutImpl(ar) ?
UnitMergeKindPersistentDefine : UnitMergeKindDefine;
m_ue.pushMergeableDef(k, name, tv);
visit(s);
continue;
}
+1
Ver Arquivo
@@ -200,6 +200,7 @@ void ProcessInit() {
*(info->m_clsPtr) = cls;
}
ClassInfo::InitializeSystemConstants();
Stack::ValidateStackSize();
SystemLib::s_inited = true;
-9
Ver Arquivo
@@ -75,15 +75,6 @@ public:
return nextElm(m_data, 0);
}
// dropContentsOnFloor twiddles the HphpArray's internal state such
// that the destructor will do (almost) no work. Only call it if
// you're 100% confident that the contents of this array are static
// or will be swept.
void dropContentsOnFloor() {
m_lastE = ElmIndEmpty;
m_data = nullptr;
}
// override/implement ArrayData api's
// these using directives ensure the full set of overloaded functions
+3 -36
Ver Arquivo
@@ -66,38 +66,6 @@ StaticString s_failure("failure");
///////////////////////////////////////////////////////////////////////////////
bool class_exists(CStrRef class_name, bool autoload /* = true */) {
return f_class_exists(class_name, autoload);
}
String get_static_class_name(CVarRef objOrClassName) {
if (objOrClassName.isString()) {
return objOrClassName.toString();
}
if (objOrClassName.isObject()) {
return objOrClassName.toObject()->o_getClassName();
}
raise_error("Class name must be a valid object or a string");
return "";
}
Variant getDynamicConstant(CVarRef v, CStrRef name) {
if (isInitialized(v)) return v;
if (AutoloadHandler::s_instance->autoloadConstant(name) &&
isInitialized(v)) {
return v;
}
raise_notice(Strings::UNDEFINED_CONSTANT,
name.c_str(), name.c_str());
return name;
}
String getUndefinedConstant(CStrRef name) {
raise_notice(Strings::UNDEFINED_CONSTANT,
name.c_str(), name.c_str());
return name;
}
bool array_is_valid_callback(CArrRef arr) {
if (arr.size() != 2 || !arr.exists(int64_t(0)) || !arr.exists(int64_t(1))) {
return false;
@@ -1255,8 +1223,7 @@ class ClassExistsChecker {
class ConstantExistsChecker {
public:
bool operator()(CStrRef name) const {
if (ClassInfo::FindConstant(name)) return true;
return g_vmContext->defined(name);
return VM::Unit::lookupCns(name.get()) != nullptr;
}
};
@@ -1315,12 +1282,12 @@ AutoloadHandler::Result AutoloadHandler::loadFromMap(CStrRef name,
}
}
bool AutoloadHandler::autoloadFunc(CStrRef name) {
bool AutoloadHandler::autoloadFunc(StringData* name) {
return !m_map.isNull() &&
loadFromMap(name, s_function, true, function_exists) != Failure;
}
bool AutoloadHandler::autoloadConstant(CStrRef name) {
bool AutoloadHandler::autoloadConstant(StringData* name) {
return !m_map.isNull() &&
loadFromMap(name, s_constant, false, ConstantExistsChecker()) != Failure;
}
+4 -6
Ver Arquivo
@@ -238,8 +238,6 @@ bool set_line(int line0, int char0 = 0, int line1 = 0, int char1 = 0);
// isset/unset
inline bool isInitialized(CVarRef v) { return v.isInitialized();}
Variant getDynamicConstant(CVarRef v, CStrRef name);
String getUndefinedConstant(CStrRef name);
inline bool isset(bool v) { return true; }
inline bool isset(char v) { return true; }
@@ -348,8 +346,8 @@ Variant &unsetLval(Array &v, const T &key) {
bool array_is_valid_callback(CArrRef arr);
bool class_exists(CStrRef class_name, bool autoload = true);
String get_static_class_name(CVarRef objOrClassName);
Variant f_call_user_func_array(CVarRef function, CArrRef params,
bool bound = false);
const HPHP::VM::Func*
vm_decode_function(CVarRef function,
@@ -568,8 +566,8 @@ public:
bool isRunning();
bool invokeHandler(CStrRef className, bool forceSplStack = false);
bool autoloadFunc(CStrRef name);
bool autoloadConstant(CStrRef name);
bool autoloadFunc(StringData* name);
bool autoloadConstant(StringData* name);
bool autoloadType(CStrRef name);
bool setMap(CArrRef map, CStrRef root);
DECLARE_STATIC_REQUEST_LOCAL(AutoloadHandler, s_instance);
+28 -7
Ver Arquivo
@@ -210,14 +210,19 @@ ClassInfo::ConstantInfo::ConstantInfo() :
valueLen(0), callback(nullptr), deferred(true) {
}
CVarRef ClassInfo::ConstantInfo::getDeferredValue() const {
assert(deferred);
if (callback) {
CVarRef (*f)()=(CVarRef(*)())callback;
return (*f)();
}
SystemGlobals* g = get_global_variables();
return g->stgv_Variant[valueLen];
}
Variant ClassInfo::ConstantInfo::getValue() const {
if (deferred) {
if (callback) {
CVarRef (*f)()=(CVarRef(*)())callback;
return (*f)();
}
SystemGlobals* g = get_global_variables();
return g->stgv_Variant[valueLen];
return getDeferredValue();
}
if (!svalue.empty()) {
try {
@@ -244,12 +249,28 @@ void ClassInfo::ConstantInfo::setStaticValue(CVarRef v) {
deferred = false;
}
void ClassInfo::InitializeSystemConstants() {
assert(s_loaded);
const ConstantMap &scm = s_systemFuncs->getConstants();
for (ConstantMap::const_iterator it = scm.begin(); it != scm.end(); ++it) {
ConstantInfo* ci = it->second;
if (ci->isDynamic()) {
VM::Unit::defDynamicSystemConstant(ci->name.get(), ci);
} else {
Variant v = ci->getValue();
bool DEBUG_ONLY res = VM::Unit::defCns(ci->name.get(),
v.asTypedValue(), true);
assert(res);
}
}
}
Array ClassInfo::GetSystemConstants() {
assert(s_loaded);
Array res;
const ConstantMap &scm = s_systemFuncs->getConstants();
for (ConstantMap::const_iterator it = scm.begin(); it != scm.end(); ++it) {
if (it->second->valueLen) {
if (!it->second->isDynamic()) {
res.set(it->second->name, it->second->getValue());
}
}
+5
Ver Arquivo
@@ -100,12 +100,16 @@ public:
const char *valueText;
const void* callback;
CVarRef getDeferredValue() const;
Variant getValue() const;
bool isDeferred() const { return deferred; }
bool isCallback() const { return callback != nullptr; }
void setValue(CVarRef value);
void setStaticValue(CVarRef value);
bool isDynamic() const {
return deferred;
}
private:
bool deferred;
Variant value;
@@ -279,6 +283,7 @@ public:
* Get all statically known system constants
*/
static Array GetSystemConstants();
static void InitializeSystemConstants();
/**
* Return all methods a class has, including the ones on base classes and
-6
Ver Arquivo
@@ -72,7 +72,6 @@ BaseExecutionContext::BaseExecutionContext() :
}
VMExecutionContext::VMExecutionContext() :
m_constants(RuntimeOption::EvalConstEstimate),
m_lambdaCounter(0), m_nesting(0),
m_injTables(nullptr), m_breakPointFilter(nullptr), m_lastLocFilter(nullptr),
m_interpreting(false), m_dbgNoBreak(false),
@@ -150,11 +149,6 @@ VMExecutionContext::~VMExecutionContext() {
decRefStr(const_cast<StringData*>(i->name));
}
}
// Any non-static contents of this array will be swept so the
// destructor doesn't need to walk the contents and clean everything
// up.
m_constants.dropContentsOnFloor();
}
void BaseExecutionContext::backupSession() {
-8
Ver Arquivo
@@ -491,8 +491,6 @@ OPCODES
void fPushObjMethodImpl(
VM::Class* cls, StringData* name, ObjectData* obj, int numArgs);
private:
HphpArray m_constants;
public:
typedef hphp_hash_map<const StringData*, ClassInfo::ConstantInfo*,
string_data_hash, string_data_same> ConstInfoMap;
@@ -603,12 +601,6 @@ public:
CStrRef getContainingFileName();
int getLine();
Array getCallerInfo();
bool defined(CStrRef name);
TypedValue* getCns(StringData* cns, bool system=true, bool dynamic=true);
bool setCns(StringData* cns, CVarRef val, bool dynamic = false);
inline bool insertCns(StringData* name, TypedValue* value) {
return m_constants.nvInsert(name, value);
}
bool renameFunction(const StringData* oldName, const StringData* newName);
bool isFunctionRenameable(const StringData* name);
void addRenameableFunctions(ArrayData* arr);
-1
Ver Arquivo
@@ -434,7 +434,6 @@ public:
F(bool, DumpAst, false) \
F(bool, MapTCHuge, true) \
F(bool, RandomHotFuncs, false) \
F(uint32_t, ConstEstimate, 10000) \
F(bool, DisableSomeRepoAuthNotices, true) \
/* */ \
+67 -12
Ver Arquivo
@@ -27,6 +27,7 @@
#include <runtime/base/runtime_error.h>
#include <runtime/base/type_conversions.h>
#include <runtime/base/builtin_functions.h>
#include <runtime/vm/translator/targetcache.h>
#include <tbb/concurrent_hash_map.h>
namespace HPHP {
@@ -35,9 +36,10 @@ IMPLEMENT_SMART_ALLOCATION_HOT(StringData);
///////////////////////////////////////////////////////////////////////////////
// constructor and destructor
// The (void *) value is not used.
typedef tbb::concurrent_hash_map<const StringData *, void *,
StringDataHashCompare> StringDataMap;
// The uint32_t is used to hold TargetCache offsets for constants
typedef tbb::concurrent_unordered_map<const StringData *, uint32_t,
string_data_hash,
string_data_same> StringDataMap;
static StringDataMap *s_stringDataMap;
size_t StringData::GetStaticStringCount() {
@@ -46,29 +48,31 @@ size_t StringData::GetStaticStringCount() {
}
StringData *StringData::GetStaticString(const StringData *str) {
StringDataMap::const_accessor acc;
if (UNLIKELY(!s_stringDataMap)) s_stringDataMap = new StringDataMap();
if (s_stringDataMap->find(acc, str)) {
return const_cast<StringData*>(acc->first);
StringDataMap::const_iterator it = s_stringDataMap->find(str);
if (it != s_stringDataMap->end()) {
return const_cast<StringData*>(it->first);
}
// Lookup failed, so do the hard work of creating a StringData with its own
// copy of the key string, so that the atomic insert() has a permanent key.
StringData *sd = (StringData*)Util::low_malloc(sizeof(StringData));
new (sd) StringData(str->data(), str->size(), CopyMalloc);
sd->setStatic();
if (!s_stringDataMap->insert(acc, sd)) {
auto pair = s_stringDataMap->insert(
std::pair<const StringData*,uint32_t>(sd, 0));
if (!pair.second) {
sd->~StringData();
Util::low_free(sd);
}
assert(acc->first != nullptr);
return const_cast<StringData*>(acc->first);
assert(pair.first->first != nullptr);
return const_cast<StringData*>(pair.first->first);
}
StringData* StringData::FindStaticString(const StringData* str) {
StringDataMap::const_accessor acc;
if (UNLIKELY(!s_stringDataMap)) s_stringDataMap = new StringDataMap();
if (s_stringDataMap->find(acc, str)) {
return const_cast<StringData*>(acc->first);
StringDataMap::const_iterator it = s_stringDataMap->find(str);
if (it != s_stringDataMap->end()) {
return const_cast<StringData*>(it->first);
}
return nullptr;
}
@@ -83,6 +87,57 @@ StringData *StringData::GetStaticString(const char *str) {
return GetStaticString(&sd);
}
uint32_t StringData::GetCnsHandle(const StringData* cnsName) {
assert(s_stringDataMap);
StringDataMap::const_iterator it = s_stringDataMap->find(cnsName);
if (it != s_stringDataMap->end()) {
return it->second;
}
return 0;
}
uint32_t StringData::DefCnsHandle(const StringData* cnsName, bool persistent) {
uint32_t val = GetCnsHandle(cnsName);
if (val) return val;
if (!cnsName->isStatic()) {
// Its a dynamic constant, that doesnt correspond to
// an already allocated handle. We'll allocate it in
// the request local TargetCache::s_constants instead.
return 0;
}
StringDataMap::iterator it = s_stringDataMap->find(cnsName);
assert(it != s_stringDataMap->end());
if (!it->second) {
VM::Transl::TargetCache::allocConstant(&it->second, persistent);
}
return it->second;
}
Array StringData::GetConstants() {
// Return an array of all defined constants.
assert(s_stringDataMap);
Array a(VM::Transl::TargetCache::s_constants);
for (StringDataMap::const_iterator it = s_stringDataMap->begin();
it != s_stringDataMap->end(); ++it) {
if (it->second) {
TypedValue& tv =
VM::Transl::TargetCache::handleToRef<TypedValue>(it->second);
if (tv.m_type != KindOfUninit) {
StrNR key(const_cast<StringData*>(it->first));
a.set(key, tvAsVariant(&tv), true);
} else if (tv.m_data.pref) {
StrNR key(const_cast<StringData*>(it->first));
ClassInfo::ConstantInfo* ci =
(ClassInfo::ConstantInfo*)(void*)tv.m_data.pref;
a.set(key, ci->getDeferredValue(), true);
}
}
}
return a;
}
void StringData::initLiteral(const char* data) {
return initLiteral(data, strlen(data));
}
+3 -1
Ver Arquivo
@@ -363,7 +363,9 @@ public:
static StringData *GetStaticString(const char *str);
static StringData *GetStaticString(char c);
static size_t GetStaticStringCount();
static uint32_t GetCnsHandle(const StringData* cnsName);
static uint32_t DefCnsHandle(const StringData* cnsName, bool persistent);
static Array GetConstants();
/**
* The order of the data members is significant. The _count field must
* be exactly FAST_REFCOUNT_OFFSET bytes from the beginning of the object.
+1 -1
Ver Arquivo
@@ -470,7 +470,7 @@ class Variant : private VariantBase {
}
// Is "define('CONSTANT', <this value>)" legal?
bool isAllowedAsConstantValue() const {
return isNull() || isScalar();
return (m_type & kNotConstantValueTypeMask) == 0;
}
bool isResource() const;
bool instanceof(CStrRef s) const;
+12 -1
Ver Arquivo
@@ -206,9 +206,20 @@ static_assert(KindOfUninit == 0,
"Several things assume this tag is 0, expecially target cache");
const unsigned int kDataTypeMask = 0x7F;
BOOST_STATIC_ASSERT(MaxNumDataTypes - 1 <= kDataTypeMask);
const unsigned int kNotConstantValueTypeMask = KindOfRef;
static_assert(kNotConstantValueTypeMask & KindOfArray &&
kNotConstantValueTypeMask & KindOfObject &&
kNotConstantValueTypeMask & KindOfRef,
"DataType & kNotConstantValueTypeMask must be non-zero for "
"Array, Object and Ref types");
static_assert(!(kNotConstantValueTypeMask &
(KindOfNull|KindOfBoolean|KindOfInt64|KindOfDouble|
KindOfStaticString|KindOfString)),
"DataType & kNotConstantValueTypeMask must be zero for "
"null, bool, int, double and string types");
// All DataTypes greater than this value are refcounted.
const DataType KindOfRefCountThreshold = KindOfStaticString;
+2 -2
Ver Arquivo
@@ -3378,7 +3378,7 @@ void c_DOMDocument::t_normalizedocument() {
bool c_DOMDocument::t_registernodeclass(CStrRef baseclass,
CStrRef extendedclass) {
if (!class_exists(baseclass)) {
if (!f_class_exists(baseclass)) {
raise_error("Class %s does not exist", baseclass.data());
return false;
}
@@ -3386,7 +3386,7 @@ bool c_DOMDocument::t_registernodeclass(CStrRef baseclass,
raise_error("Class %s is not DOMNode or derived from it.", baseclass.data());
return false;
}
if (!class_exists(extendedclass)) {
if (!f_class_exists(extendedclass)) {
raise_error("Class %s does not exist", extendedclass.data());
return false;
}
+1 -1
Ver Arquivo
@@ -46,7 +46,7 @@ bool f_function_exists(CStrRef function_name, bool autoload /* = true */) {
return
function_exists(function_name) ||
(autoload &&
AutoloadHandler::s_instance->autoloadFunc(function_name) &&
AutoloadHandler::s_instance->autoloadFunc(function_name.get()) &&
function_exists(function_name));
}
+5 -18
Ver Arquivo
@@ -109,12 +109,7 @@ Variant f_constant(CStrRef name) {
raise_warning("Couldn't find constant %s", data);
return uninit_null();
} else {
TypedValue* cns = g_vmContext->getCns(name.get());
if (cns == NULL) {
if (AutoloadHandler::s_instance->autoloadConstant(name)) {
cns = g_vmContext->getCns(name.get());
}
}
TypedValue* cns = VM::Unit::loadCns(name.get());
if (cns) return tvAsVariant(cns);
return uninit_null();
}
@@ -125,9 +120,7 @@ bool f_define(CStrRef name, CVarRef value,
if (case_insensitive) {
raise_warning(Strings::CONSTANTS_CASE_SENSITIVE);
}
// TODO: Once we're inlining constants from hphpc this should
// fatal or fail in some other way.
return g_vmContext->setCns(name.get(), value, true);
return VM::Unit::defCns(name.get(), value.getTypedAccessor());
}
bool f_defined(CStrRef name, bool autoload /* = true */) {
@@ -146,15 +139,9 @@ bool f_defined(CStrRef name, bool autoload /* = true */) {
}
return false;
} else {
// system/uniquely defined scalar constant
if (ClassInfo::FindConstant(name)) return true;
if (g_vmContext->defined(name)) {
return true;
}
if (!autoload || !AutoloadHandler::s_instance->autoloadConstant(name)) {
return false;
}
return g_vmContext->defined(name);
return autoload ?
VM::Unit::loadCns(name.get()) :
VM::Unit::lookupCns(name.get());
}
}
+1 -1
Ver Arquivo
@@ -144,7 +144,7 @@ Array f_get_defined_constants(CVarRef categorize /* = null_variant */) {
throw NotSupportedException(__func__, "constant categorization not "
"supported");
}
return ClassInfo::GetConstants();
return StringData::GetConstants();
}
String f_get_include_path() {
+5 -5
Ver Arquivo
@@ -588,7 +588,7 @@ static void pdo_stmt_construct(sp_PDOStatement stmt, Object object,
static bool valid_statement_class(sp_PDOConnection dbh, CVarRef opt,
String &clsname, Variant &ctor_args) {
if (!opt.isArray() || !opt.toArray().exists(0) || !opt[0].isString() ||
!class_exists(opt[0])) {
!f_class_exists(opt[0])) {
pdo_raise_impl_error
(dbh, NULL, "HY000",
"PDO::ATTR_STATEMENT_CLASS requires format array(classname, "
@@ -812,7 +812,7 @@ static bool pdo_stmt_set_fetch_mode(sp_PDOStatement stmt, int _argc, int64_t mod
pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
"classname must be a string");
} else {
retval = class_exists(_argv[0]);
retval = f_class_exists(_argv[0]);
if (retval) {
stmt->fetch.clsname = _argv[0].toString();
}
@@ -1805,7 +1805,7 @@ static bool do_fetch(sp_PDOStatement stmt, bool do_bind, Variant &ret,
Variant val;
fetch_value(stmt, val, i++, NULL);
if (!val.isNull()) {
if (!class_exists(val)) {
if (!f_class_exists(val)) {
stmt->fetch.clsname = "stdclass";
} else {
stmt->fetch.clsname = val.toString();
@@ -2711,7 +2711,7 @@ Variant c_PDOStatement::t_fetchobject(CStrRef class_name /* = null_string */,
if (class_name.isNull()) {
m_stmt->fetch.clsname = "stdclass";
}
if (!class_exists(m_stmt->fetch.clsname)) {
if (!f_class_exists(m_stmt->fetch.clsname)) {
pdo_raise_impl_error(m_stmt->dbh, m_stmt, "HY000",
"Could not find user-supplied class");
error = true;
@@ -2768,7 +2768,7 @@ Variant c_PDOStatement::t_fetchall(int64_t how /* = 0 */,
if (class_name.isNull()) {
m_stmt->fetch.clsname = "stdclass";
}
if (!class_exists(m_stmt->fetch.clsname)) {
if (!f_class_exists(m_stmt->fetch.clsname)) {
pdo_raise_impl_error(m_stmt->dbh, m_stmt, "HY000",
"Could not find user-supplied class");
error = 1;
+2 -1
Ver Arquivo
@@ -17,6 +17,7 @@
#include <runtime/ext/ext_simplexml.h>
#include <runtime/ext/ext_file.h>
#include <runtime/ext/ext_class.h>
#include <runtime/base/class_info.h>
#include <runtime/base/util/request_local.h>
@@ -239,7 +240,7 @@ Variant f_simplexml_load_string(CStrRef data,
int64_t options /* = 0 */,
CStrRef ns /* = "" */,
bool is_prefix /* = false */) {
if (!class_exists(class_name)) {
if (!f_class_exists(class_name)) {
throw_invalid_argument("class %s does not exist", class_name.data());
return uninit_null();
}
+1 -1
Ver Arquivo
@@ -1926,7 +1926,7 @@ void c_SoapServer::t___construct(CVarRef wsdl,
void c_SoapServer::t_setclass(int _argc, CStrRef name,
CArrRef _argv /* = null_array */) {
SoapServerScope ss(this);
if (class_exists(name, true)) {
if (f_class_exists(name, true)) {
m_type = SOAP_CLASS;
m_soap_class.name = name;
m_soap_class.argv = _argv;
+1 -1
Ver Arquivo
@@ -48,7 +48,7 @@ void skip_element(long thrift_typeID, PHPInputTransport& transport);
// Create a PHP object given a typename and call the ctor, optionally passing up to 2 arguments
Object createObject(CStrRef obj_typename, int nargs = 0,
CVarRef arg1 = null_variant, CVarRef arg2 = null_variant) {
if (!class_exists(obj_typename)) {
if (!f_class_exists(obj_typename)) {
raise_warning("runtime/ext_thrift: Class %s does not exist",
obj_typename.data());
return Object();
+8 -72
Ver Arquivo
@@ -1515,64 +1515,6 @@ Array VMExecutionContext::getCallerInfo() {
return result;
}
bool VMExecutionContext::defined(CStrRef name) {
return m_constants.nvGet(name.get()) != nullptr;
}
TypedValue* VMExecutionContext::getCns(StringData* cns,
bool system /* = true */,
bool dynamic /* = true */) {
if (dynamic) {
TypedValue* tv = m_constants.nvGet(cns);
if (tv != nullptr) {
return tv;
}
}
if (system) {
const ClassInfo::ConstantInfo* ci = ClassInfo::FindConstant(cns->data());
if (ci != nullptr) {
if (!dynamic) {
ConstInfoMap::const_iterator it = m_constInfo.find(cns);
if (it != m_constInfo.end()) {
// This is a dynamic constant, so don't report it.
assert(ci == it->second);
return nullptr;
}
}
TypedValue tv;
tvWriteUninit(&tv);
tvAsVariant(&tv) = ci->getValue();
m_constants.nvSet(cns, &tv, false);
tvRefcountedDecRef(&tv);
return m_constants.nvGet(cns);
}
}
return nullptr;
}
bool VMExecutionContext::setCns(StringData* cns, CVarRef val, bool dynamic) {
if (m_constants.nvGet(cns) != nullptr ||
ClassInfo::FindConstant(cns->data()) != nullptr) {
raise_warning(Strings::CONSTANT_ALREADY_DEFINED, cns->data());
return false;
}
if (!val.isAllowedAsConstantValue()) {
raise_warning(Strings::CONSTANTS_MUST_BE_SCALAR);
return false;
}
const_cast<Variant&>(val).setEvalScalar();
TypedValue* tv = val.getTypedAccessor();
m_constants.nvSet(cns, tv, false);
assert(m_constants.nvGet(cns) != nullptr);
if (RuntimeOption::EvalJit) {
if (dynamic) {
newPreConst(cns, *tv);
}
tx64->defineCns(cns);
}
return true;
}
void VMExecutionContext::newPreConst(StringData* name,
const TypedValue& val) {
name->incRefCount();
@@ -2507,7 +2449,7 @@ Array VMExecutionContext::getUserFunctionsInfo() {
Array VMExecutionContext::getConstantsInfo() {
// Return an array of all defined constant:value pairs. This method is used
// to support get_defined_constants().
return Array(m_constants.copy());
return Array::Create();
}
const ClassInfo::MethodInfo* VMExecutionContext::findFunctionInfo(
@@ -2593,7 +2535,7 @@ const ClassInfo* VMExecutionContext::findTraitInfo(CStrRef name) {
const ClassInfo::ConstantInfo* VMExecutionContext::findConstantInfo(
CStrRef name) {
TypedValue* tv = m_constants.nvGet(name.get());
TypedValue* tv = Unit::lookupCns(name.get());
if (tv == nullptr) {
return nullptr;
}
@@ -3849,17 +3791,12 @@ inline void OPTBLD_INLINE VMExecutionContext::iopColAddElemC(PC& pc) {
inline void OPTBLD_INLINE VMExecutionContext::iopCns(PC& pc) {
NEXT();
DECODE_LITSTR(s);
TypedValue* cns = getCns(s);
TypedValue* cns = Unit::loadCns(s);
if (cns == nullptr) {
if (AutoloadHandler::s_instance->autoloadConstant(StrNR(s))) {
cns = getCns(s);
}
if (!cns) {
raise_notice(Strings::UNDEFINED_CONSTANT,
s->data(), s->data());
m_stack.pushStaticString(s);
return;
}
raise_notice(Strings::UNDEFINED_CONSTANT,
s->data(), s->data());
m_stack.pushStaticString(s);
return;
}
Cell* c1 = m_stack.allocC();
tvReadCell(cns, c1);
@@ -3869,8 +3806,7 @@ inline void OPTBLD_INLINE VMExecutionContext::iopDefCns(PC& pc) {
NEXT();
DECODE_LITSTR(s);
TypedValue* tv = m_stack.topTV();
tvAsVariant(tv) = setCns(s, tvAsCVarRef(tv));
tvAsVariant(tv) = Unit::defCns(s, tv);
}
inline void OPTBLD_INLINE VMExecutionContext::iopClsCns(PC& pc) {
+3 -8
Ver Arquivo
@@ -4700,18 +4700,13 @@ void CodeGenerator::cgDefCns(IRInstruction* inst) {
UNUSED SSATmp* cnsName = inst->getSrc(0);
UNUSED SSATmp* val = inst->getSrc(1);
using namespace TargetCache;
UNUSED CacheHandle ch = allocConstant((StringData*)cnsName->getValStr());
#if 0
UNUSED CacheHandle ch = allocConstant((StringData*)cnsName->getValStr());
// ALIA:TODO
// XXX second param is an inout pointer to a Ref, so we need to pass
// the pointer to a stack slot
if (RuntimeOption::RepoAuthoritative) {
EMIT_CALL3(a, defCnsHelper<false>, IMM(ch), A(i.inputs[0]->location),
IMM((uint64_t)name));
} else {
EMIT_CALL4(a, defCnsHelper<true>, IMM(ch), A(i.inputs[0]->location),
IMM((uint64_t)name), IMM(allocCnsBit(name)));
}
EMIT_CALL3(a, defCnsHelper, IMM(ch), A(i.inputs[0]->location),
IMM((uint64_t)name));
#endif
CG_PUNT(DefCns);
+15 -18
Ver Arquivo
@@ -95,6 +95,7 @@ undefinedError(const char* msg, const char* name) {
// Targetcache memory. See the comment in targetcache.h
__thread void* tl_targetCaches = nullptr;
__thread HphpArray* s_constants = nullptr;
static_assert(kConditionFlagsOff + sizeof(ssize_t) <= 64,
"kConditionFlagsOff too large");
@@ -195,10 +196,6 @@ size_t allocBit() {
return allocBitImpl(nullptr, NSInvalid);
}
size_t allocCnsBit(const StringData* name) {
return allocBitImpl(name, NSCnsBits);
}
Handle bitOffToHandleAndMask(size_t bit, uint8_t &mask) {
static_assert(!(8 % CHAR_BIT), "Unexpected size of char");
mask = (uint8_t)1 << (bit % 8);
@@ -353,6 +350,7 @@ static const bool zeroViaMemset = true;
void
requestInit() {
assert(tl_targetCaches);
assert(!s_constants);
TRACE(1, "TargetCache: @%p\n", tl_targetCaches);
if (zeroViaMemset) {
TRACE(1, "TargetCache: bzeroing %zd bytes: %p\n", s_frontier,
@@ -366,6 +364,7 @@ requestExit() {
if (!zeroViaMemset) {
flush();
}
s_constants = nullptr; // it will be swept
}
void
@@ -795,23 +794,21 @@ ClassCache::lookup(Handle handle, StringData *name,
* definition is hooked in the runtime to allocate and update these
* structures.
*/
CacheHandle allocConstant(StringData* name) {
BOOST_STATIC_ASSERT(KindOfUninit == 0);
return namedAlloc<NSConstant>(name, sizeof(TypedValue), sizeof(TypedValue));
CacheHandle allocConstant(uint32_t* handlep, bool persistent) {
if (UNLIKELY(!*handlep)) {
Lock l(s_handleMutex);
if (!*handlep) {
*handlep =
allocLocked(persistent, sizeof(TypedValue), sizeof(TypedValue));
}
}
return *handlep;
}
CacheHandle allocStatic() {
return namedAlloc<NSInvalid>(nullptr, sizeof(TypedValue*), sizeof(TypedValue*));
}
void
fillConstant(StringData* name) {
assert(name);
Handle ch = allocConstant(name);
testAndSetBit(allocCnsBit(name));
TypedValue *val = g_vmContext->getCns(name);
assert(val);
*(TypedValue*)handleToPtr(ch) = *val;
return namedAlloc<NSInvalid>(nullptr, sizeof(TypedValue*),
sizeof(TypedValue*));
}
CacheHandle allocClassConstant(StringData* name) {
+7 -8
Ver Arquivo
@@ -43,6 +43,11 @@ extern size_t s_frontier;
extern size_t s_persistent_frontier;
extern size_t s_persistent_start;
/*
* Array of dynamically defined constants
*/
extern __thread HphpArray* s_constants;
static const int kConditionFlagsOff = 0;
/*
@@ -94,7 +99,6 @@ CacheHandle namedAlloc(const StringData* name, int numBytes, int align) {
}
size_t allocBit();
size_t allocCnsBit(const StringData* name);
CacheHandle bitOffToHandleAndMask(size_t bit, uint8_t &mask);
bool testBit(CacheHandle handle, uint32_t mask);
bool testBit(size_t bit);
@@ -326,13 +330,9 @@ CacheHandle allocNameDef(const NamedEntity* name);
/*
* Constants.
*
* The request-private value of a constant. This one is a bit
* different: we don't record a key, per se, expecting the translation
* to remember it for us. When the targetcache area gets reset, the
* translator must call fillConstant again.
* The request-private value of a constant.
*/
CacheHandle allocConstant(StringData* name);
void fillConstant(StringData* name);
CacheHandle allocConstant(uint32_t* handlep, bool persistent);
CacheHandle allocClassConstant(StringData* name);
TypedValue* lookupClassConstant(TypedValue* cache,
@@ -361,7 +361,6 @@ private:
static inline SPropCache* cacheAtHandle(CacheHandle handle) {
return (SPropCache*)(uintptr_t(tl_targetCaches) + handle);
}
CacheHandle allocConstantLocked(StringData* name);
public:
TypedValue* m_tv; // public; it is used from TC and we assert the offset
static CacheHandle alloc(const StringData* sd = nullptr) {
+85 -97
Ver Arquivo
@@ -5574,21 +5574,31 @@ TranslatorX64::translateColAddElemC(const Tracelet& t,
A(valLoc));
}
static void undefCns(const StringData* nm) {
VMRegAnchor _;
TypedValue *cns = g_vmContext->getCns(const_cast<StringData*>(nm));
if (!cns) {
if (AutoloadHandler::s_instance->autoloadConstant(StrNR(nm))) {
cns = g_vmContext->getCns(const_cast<StringData*>(nm));
static int64_t undefCns(const TypedValue* tv, const StringData* nm, Cell* c1) {
assert(tv->m_type == KindOfUninit);
TypedValue *cns = nullptr;
if (UNLIKELY(tv->m_data.pref != nullptr)) {
ClassInfo::ConstantInfo* ci =
(ClassInfo::ConstantInfo*)(void*)tv->m_data.pref;
cns = const_cast<Variant&>(ci->getDeferredValue()).asTypedValue();
tvReadCell(cns, c1);
} else {
if (UNLIKELY(TargetCache::s_constants != nullptr)) {
cns = TargetCache::s_constants->HphpArray::nvGet(nm);
}
if (!cns) {
cns = Unit::loadCns(const_cast<StringData*>(nm));
}
if (UNLIKELY(!cns)) {
raise_notice(Strings::UNDEFINED_CONSTANT, nm->data(), nm->data());
g_vmContext->getStack().pushStringNoRc(const_cast<StringData*>(nm));
return;
c1->m_data.pstr = const_cast<StringData*>(nm);
c1->m_type = BitwiseKindOfString;
} else {
c1->m_type = cns->m_type;
c1->m_data = cns->m_data;
}
}
Cell* c1 = g_vmContext->getStack().allocC();
tvReadCell(cns, c1);
return c1->m_type;
}
void TranslatorX64::emitSideExit(Asm& a, const NormalizedInstruction& i,
@@ -5626,36 +5636,37 @@ TranslatorX64::translateCns(const Tracelet& t,
// be.
DataType outType = i.outStack->valueType();
StringData* name = curUnit()->lookupLitstrId(i.imm[0].u_SA);
const TypedValue* tv = g_vmContext->getCns(name, true, false);
const TypedValue* tv = Unit::lookupPersistentCns(name);
bool checkDefined = false;
if (outType != KindOfInvalid && tv == nullptr &&
!RuntimeOption::RepoAuthoritative) {
PreConstDepMap::accessor acc;
tv = findUniquePreConst(acc, name);
if (tv != nullptr) {
checkDefined = true;
acc->second.srcKeys.insert(t.m_sk);
Stats::emitInc(a, Stats::Tx64_CnsFast);
} else {
// We had a unique value while analyzing but don't anymore. This
// should be rare so just punt to keep things simple.
punt();
if (tv) {
// KindOfUninit is for a small number of "dynamic"
// system constants
checkDefined = tv->m_type == KindOfUninit;
} else {
if (outType != KindOfInvalid &&
!RuntimeOption::RepoAuthoritative) {
PreConstDepMap::accessor acc;
tv = findUniquePreConst(acc, name);
if (tv != nullptr) {
checkDefined = true;
acc->second.srcKeys.insert(t.m_sk);
Stats::emitInc(a, Stats::Tx64_CnsFast);
} else {
// We had a unique value while analyzing but don't anymore. This
// should be rare so just punt to keep things simple.
punt();
}
}
}
using namespace TargetCache;
if (tv && tvIsStatic(tv)) {
m_regMap.allocOutputRegs(i);
ScratchReg ret(m_regMap);
boost::scoped_ptr<DiamondReturn> astubsRet;
m_regMap.invalidate(i.outStack->location);
if (checkDefined) {
size_t bit = allocCnsBit(name);
uint8_t mask;
CacheHandle ch = bitOffToHandleAndMask(bit, mask);
// The 'test' instruction takes a signed immediate and the mask is
// unsigned, but everything works out okay because the immediate is
// the same size as the other operand. However, we have to sign-extend
// the mask to 64 bits to make the assembler happy.
int64_t imm = (int64_t)(int8_t)mask;
a.testb(imm, rVmTl[ch]);
CacheHandle ch = StringData::GetCnsHandle(name);
assert(ch);
emitCmpTVType(a, KindOfUninit, rVmTl[ch + TVOFF(m_type)]);
if (!i.next) astubsRet.reset(new DiamondReturn);
{
// If we get to the optimistic translation and the constant
@@ -5666,31 +5677,46 @@ TranslatorX64::translateCns(const Tracelet& t,
// enough that it's not worth the complexity.
UnlikelyIfBlock ifZero(CC_Z, a, astubs, astubsRet.get());
Stats::emitInc(astubs, Stats::Tx64_CnsFast, -1);
EMIT_CALL(astubs, undefCns, IMM((uintptr_t)name));
EMIT_CALL(astubs, undefCns,
RPLUS(rVmTl, ch),
IMM((uintptr_t)name),
A(i.outStack->location));
recordReentrantStubCall(i);
if (i.next) {
emitMovRegReg(astubs, rax, r(ret));
ifZero.reconcileEarly();
astubs.cmp_imm32_reg64(outType, r(ret));
astubs.je(a.code.frontier);
// Now we're definitely exiting.
// Save it, and thaw
RegAlloc save = m_regMap;
m_regMap.defrost();
emitSideExit(astubs, i, true);
m_regMap = save;
} else {
m_regMap.invalidate(i.outStack->location);
// DiamondReturn will take care of branching
// to the return, below
}
}
} else {
// Its type and value are known at compile-time.
assert(tv->m_type == outType ||
(IS_STRING_TYPE(tv->m_type) && IS_STRING_TYPE(outType)));
// tv is static; no need to incref
}
// Its type and value are known at compile-time.
assert(tv->m_type == outType ||
(IS_STRING_TYPE(tv->m_type) && IS_STRING_TYPE(outType)));
m_regMap.allocOutputRegs(i);
PhysReg r = getReg(i.outStack->location);
a. movq (tv->m_data.num, r);
// tv is static; no need to incref
return;
}
Stats::emitInc(a, Stats::Tx64_CnsSlow);
CacheHandle ch = allocConstant(name);
CacheHandle ch = StringData::DefCnsHandle(name, false);
TRACE(2, "Cns: %s -> ch %" PRId64 "\n", name->data(), ch);
// Load the constant out of the thread-private tl_targetCaches.
ScratchReg cns(m_regMap);
a. lea_reg64_disp_reg64(rVmTl, ch, r(cns));
emitCmpTVType(a, 0, r(cns)[TVOFF(m_type)]);
emitCmpTVType(a, KindOfUninit, r(cns)[TVOFF(m_type)]);
DiamondReturn astubsRet;
int stackDest = 0 - int(sizeof(Cell)); // popped - pushed
{
@@ -5698,7 +5724,10 @@ TranslatorX64::translateCns(const Tracelet& t,
// at least stackDest and tmp are specific to the translation
// context.
UnlikelyIfBlock ifb(CC_Z, a, astubs, &astubsRet);
EMIT_CALL(astubs, undefCns, IMM((uintptr_t)name));
EMIT_CALL(astubs, undefCns,
R(r(cns)),
IMM((uintptr_t)name),
A(i.outStack->location));
recordReentrantStubCall(i);
m_regMap.invalidate(i.outStack->location);
}
@@ -5713,70 +5742,32 @@ TranslatorX64::analyzeDefCns(Tracelet& t,
NormalizedInstruction& i) {
StringData* name = curUnit()->lookupLitstrId(i.imm[0].u_SA);
/* don't bother to translate if it names a builtin constant */
i.m_txFlags = supportedPlan(!g_vmContext->getCns(name, true, false));
}
typedef void (*defCnsHelper_func_t)(TargetCache::CacheHandle ch, Variant *inout,
StringData *name, size_t bit);
template<bool setBit>
static void defCnsHelper(TargetCache::CacheHandle ch, Variant *inout,
StringData *name, size_t bit) {
using namespace TargetCache;
TypedValue *tv = (TypedValue*)handleToPtr(ch);
if (LIKELY(tv->m_type == KindOfUninit &&
inout->isAllowedAsConstantValue())) {
inout->setEvalScalar();
if (LIKELY(g_vmContext->insertCns(name, (TypedValue*)inout))) {
tvDup((TypedValue*)inout, tv);
*inout = true;
if (setBit) {
DEBUG_ONLY bool alreadyDefined = testAndSetBit(bit);
assert(!alreadyDefined);
}
return;
}
tv = (TypedValue*)&false_varNR;
}
if (tv->m_type != KindOfUninit) {
raise_warning(Strings::CONSTANT_ALREADY_DEFINED, name->data());
} else {
assert(!inout->isAllowedAsConstantValue());
raise_warning(Strings::CONSTANTS_MUST_BE_SCALAR);
}
*inout = false;
i.m_txFlags = supportedPlan(!Unit::lookupPersistentCns(name));
}
void
TranslatorX64::translateDefCns(const Tracelet& t,
const NormalizedInstruction& i) {
using namespace TargetCache;
StringData* name = curUnit()->lookupLitstrId(i.imm[0].u_SA);
CacheHandle ch = StringData::DefCnsHandle(name, false);
if (false) {
TargetCache::CacheHandle ch = 0;
size_t bit = 0;
Variant *inout = 0;
StringData *name = 0;
defCnsHelper<true>(ch, inout, name, bit);
defCnsHelper<false>(ch, inout, name, bit);
TypedValue *value = 0;
Unit::defCnsHelper(ch, value, name);
}
using namespace TargetCache;
CacheHandle ch = allocConstant(name);
TRACE(2, "DefCns: %s -> ch %" PRId64 "\n", name->data(), ch);
m_regMap.cleanLoc(i.inputs[0]->location);
if (RuntimeOption::RepoAuthoritative) {
EMIT_CALL(a, (defCnsHelper_func_t)defCnsHelper<false>,
IMM(ch), A(i.inputs[0]->location),
IMM((uint64_t)name));
} else {
EMIT_CALL(a, (defCnsHelper_func_t)defCnsHelper<true>,
IMM(ch), A(i.inputs[0]->location),
IMM((uint64_t)name), IMM(allocCnsBit(name)));
}
EMIT_CALL(a, Unit::defCnsHelper,
IMM(ch), A(i.inputs[0]->location),
IMM((uint64_t)name));
recordReentrantCall(i);
m_regMap.invalidate(i.outStack->location);
m_regMap.bind(rax, i.outStack->location, i.outStack->outerType(),
RegInfo::DIRTY);
}
void
@@ -9721,7 +9712,8 @@ static const Func* autoloadMissingFunc(const StringData* funcName,
TargetCache::CacheHandle ch,
bool safe) {
VMRegAnchor _;
AutoloadHandler::s_instance->autoloadFunc(funcName->data());
AutoloadHandler::s_instance->autoloadFunc(
const_cast<StringData*>(funcName));
Func* toCall = *(Func**)TargetCache::handleToPtr(ch);
/* toCall could be a different function due to renaming */
if (toCall) {
@@ -12126,10 +12118,6 @@ void TranslatorX64::recordGdbStub(const X64Assembler& a,
}
}
void TranslatorX64::defineCns(StringData* name) {
TargetCache::fillConstant(name);
}
size_t TranslatorX64::getCodeSize() {
return a.code.frontier - a.code.base;
}
@@ -1033,9 +1033,6 @@ public:
// Called at the end of eval()
void requestExit();
// Called when name is bound to a value
void defineCns(StringData* name);
// Returns a string with cache usage information
virtual std::string getUsage();
virtual size_t getCodeSize();
+2 -2
Ver Arquivo
@@ -721,11 +721,11 @@ getDynLocType(const vector<DynLocation*>& inputs,
// to the targetcache if none exists.
StringData *sd = curUnit()->lookupLitstrId(ni->imm[0].u_SA);
assert(sd);
const TypedValue* tv = g_vmContext->getCns(sd, true, false);
const TypedValue* tv = Unit::lookupPersistentCns(sd);
if (tv) {
return RuntimeType(tv->m_type);
}
tv = g_vmContext->getCns(sd);
tv = Unit::lookupCns(sd);
if (tv) {
ni->outputPredicted = true;
TRACE(1, "CNS %s: guessing runtime type %d\n", sd->data(), tv->m_type);
-1
Ver Arquivo
@@ -901,7 +901,6 @@ public:
virtual TCA funcPrologue(Func* f, int nArgs, ActRec* ar = nullptr) = 0;
virtual TCA getCallToExit() = 0;
virtual TCA getRetFromInterpretedFrame() = 0;
virtual void defineCns(StringData* name) = 0;
virtual std::string getUsage() = 0;
virtual size_t getCodeSize() = 0;
virtual size_t getStubSize() = 0;
+152 -35
Ver Arquivo
@@ -761,6 +761,16 @@ void Unit::loadFunc(const Func *func) {
const_cast<Func*>(func)->m_cachedOffset = ne->m_cachedFuncOffset;
}
static void mergeCns(TypedValue& tv, TypedValue *value,
StringData *name) {
if (LIKELY(tv.m_type == KindOfUninit)) {
tv = *value;
return;
}
raise_warning(Strings::CONSTANT_ALREADY_DEFINED, name->data());
}
static SimpleMutex unitInitLock(false /* reentrant */, RankUnitInit);
void Unit::initialMerge() {
@@ -799,13 +809,13 @@ void Unit::initialMerge() {
* the pointer will be followed by a TypedValue representing
* the value being defined/assigned.
*/
bool allClassesUnique = true;
bool needsCompact = false;
int ix = m_mergeInfo->m_firstHoistablePreClass;
int end = m_mergeInfo->m_firstMergeablePreClass;
while (ix < end) {
PreClass* pre = (PreClass*)m_mergeInfo->mergeableObj(ix++);
if (allClassesUnique) {
allClassesUnique = pre->attrs() & AttrUnique;
if (pre->attrs() & AttrUnique) {
needsCompact = true;
}
}
if (isMergeOnly()) {
@@ -819,8 +829,8 @@ void Unit::initialMerge() {
case UnitMergeKindDone:
not_reached();
case UnitMergeKindClass:
if (allClassesUnique) {
allClassesUnique = ((PreClass*)obj)->attrs() & AttrUnique;
if (((PreClass*)obj)->attrs() & AttrUnique) {
needsCompact = true;
}
break;
case UnitMergeKindReqDoc: {
@@ -833,11 +843,18 @@ void Unit::initialMerge() {
m_mergeInfo->mergeableObj(ix) = (void*)((char*)unit + (int)k);
break;
}
case UnitMergeKindPersistentDefine:
needsCompact = true;
case UnitMergeKindDefine: {
StringData* s = (StringData*)((char*)obj - (int)k);
auto* v = (TypedValueAux*) m_mergeInfo->mergeableData(ix + 1);
ix += sizeof(*v) / sizeof(void*);
v->cacheHandle() = TargetCache::allocConstant(s);
v->cacheHandle() = StringData::DefCnsHandle(
s, k == UnitMergeKindPersistentDefine);
if (k == UnitMergeKindPersistentDefine) {
mergeCns(TargetCache::handleToRef<TypedValue>(v->cacheHandle()),
v, s);
}
break;
}
case UnitMergeKindGlobal: {
@@ -851,21 +868,97 @@ void Unit::initialMerge() {
ix++;
}
}
if (allClassesUnique) state |= UnitMergeStateUniqueClasses;
if (needsCompact) state |= UnitMergeStateNeedsCompact;
}
m_mergeState = UnitMergeStateMerged | state;
}
}
static void mergeCns(TypedValue& tv, TypedValue *value,
StringData *name) {
if (LIKELY(tv.m_type == KindOfUninit &&
g_vmContext->insertCns(name, value))) {
tvDup(value, &tv);
return;
TypedValue* Unit::lookupCns(const StringData* cnsName) {
TargetCache::CacheHandle handle = StringData::GetCnsHandle(cnsName);
if (LIKELY(handle != 0)) {
TypedValue& tv = TargetCache::handleToRef<TypedValue>(handle);
if (LIKELY(tv.m_type != KindOfUninit)) return &tv;
if (UNLIKELY(tv.m_data.pref != nullptr)) {
ClassInfo::ConstantInfo* ci =
(ClassInfo::ConstantInfo*)(void*)tv.m_data.pref;
return const_cast<Variant&>(ci->getDeferredValue()).asTypedValue();
}
}
if (UNLIKELY(TargetCache::s_constants != nullptr)) {
return TargetCache::s_constants->HphpArray::nvGet(cnsName);
}
return nullptr;
}
raise_warning(Strings::CONSTANT_ALREADY_DEFINED, name->data());
TypedValue* Unit::lookupPersistentCns(const StringData* cnsName) {
TargetCache::CacheHandle handle = StringData::GetCnsHandle(cnsName);
if (!TargetCache::isPersistentHandle(handle)) return nullptr;
return &TargetCache::handleToRef<TypedValue>(handle);
}
TypedValue* Unit::loadCns(const StringData* cnsName) {
TypedValue* tv = lookupCns(cnsName);
if (LIKELY(tv != nullptr)) return tv;
if (!AutoloadHandler::s_instance->autoloadConstant(
const_cast<StringData*>(cnsName))) {
return nullptr;
}
return lookupCns(cnsName);
}
bool Unit::defCns(const StringData* cnsName, const TypedValue* value,
bool persistent /* = false */) {
TargetCache::CacheHandle handle =
StringData::DefCnsHandle(cnsName, persistent);
if (UNLIKELY(handle == 0)) {
if (UNLIKELY(!TargetCache::s_constants)) {
/*
* This only happens when we call define on a non
* static string. Not worth presizing or otherwise
* optimizing for.
*/
TargetCache::s_constants = NEW(HphpArray)(1);
TargetCache::s_constants->incRefCount();
}
if (TargetCache::s_constants->nvInsert(
const_cast<StringData*>(cnsName), const_cast<TypedValue*>(value))) {
return true;
}
raise_warning(Strings::CONSTANT_ALREADY_DEFINED, cnsName->data());
return false;
}
return defCnsHelper(handle, value, cnsName);
}
uint64_t Unit::defCnsHelper(uint64_t ch,
const TypedValue *value,
const StringData *cnsName) {
TypedValue* cns = &TargetCache::handleToRef<TypedValue>(ch);
if (UNLIKELY(cns->m_type != KindOfUninit) ||
UNLIKELY(cns->m_data.pref != nullptr)) {
raise_warning(Strings::CONSTANT_ALREADY_DEFINED, cnsName->data());
} else if (UNLIKELY(!tvAsCVarRef(value).isAllowedAsConstantValue())) {
raise_warning(Strings::CONSTANTS_MUST_BE_SCALAR);
} else {
Variant v = tvAsCVarRef(value);
v.setEvalScalar();
cns->m_data = v.asTypedValue()->m_data;
cns->m_type = v.asTypedValue()->m_type;
return true;
}
return false;
}
void Unit::defDynamicSystemConstant(const StringData* cnsName,
const void* data) {
TargetCache::CacheHandle handle =
StringData::DefCnsHandle(cnsName, true);
assert(handle);
TypedValue* cns = &TargetCache::handleToRef<TypedValue>(handle);
assert(cns->m_type == KindOfUninit);
cns->m_data.pref = (RefData*)data;
}
static void setGlobal(void* cacheAddr, TypedValue *value,
@@ -937,13 +1030,15 @@ size_t compactUnitMergeInfo(UnitMergeInfo* in, UnitMergeInfo* out) {
void* obj = in->mergeableObj(ix);
assert((uintptr_t(obj) & 1) == 0);
PreClass* pre = (PreClass*)obj;
Class* cls = pre->namedEntity()->clsList();
assert(cls && !cls->m_nextClass);
assert(cls->preClass() == pre);
if (TargetCache::isPersistentHandle(cls->m_cachedOffset)) {
delta++;
} else if (out) {
out->mergeableObj(oix++) = (void*)(uintptr_t(cls) | 1);
if (pre->attrs() & AttrUnique) {
Class* cls = pre->namedEntity()->clsList();
assert(cls && !cls->m_nextClass);
assert(cls->preClass() == pre);
if (TargetCache::isPersistentHandle(cls->m_cachedOffset)) {
delta++;
} else if (out) {
out->mergeableObj(oix++) = (void*)(uintptr_t(cls) | 1);
}
}
}
@@ -958,20 +1053,27 @@ size_t compactUnitMergeInfo(UnitMergeInfo* in, UnitMergeInfo* out) {
switch (k) {
case UnitMergeKindClass: {
PreClass* pre = (PreClass*)obj;
Class* cls = pre->namedEntity()->clsList();
assert(cls && !cls->m_nextClass);
assert(cls->preClass() == pre);
if (TargetCache::isPersistentHandle(cls->m_cachedOffset)) {
delta++;
} else if (out) {
out->mergeableObj(oix++) =
(void*)(uintptr_t(cls) | UnitMergeKindUniqueDefinedClass);
if (pre->attrs() & AttrUnique) {
Class* cls = pre->namedEntity()->clsList();
assert(cls && !cls->m_nextClass);
assert(cls->preClass() == pre);
if (TargetCache::isPersistentHandle(cls->m_cachedOffset)) {
delta++;
} else if (out) {
out->mergeableObj(oix++) =
(void*)(uintptr_t(cls) | UnitMergeKindUniqueDefinedClass);
}
}
break;
}
case UnitMergeKindUniqueDefinedClass:
not_reached();
case UnitMergeKindPersistentDefine:
delta += 1 + sizeof(TypedValueAux) / sizeof(void*);
ix += sizeof(TypedValueAux) / sizeof(void*);
break;
case UnitMergeKindDefine:
case UnitMergeKindGlobal:
if (out) {
@@ -1151,6 +1253,17 @@ void Unit::mergeImpl(void* tcbase, UnitMergeInfo* mi) {
} while (k == UnitMergeKindUniqueDefinedClass);
continue;
case UnitMergeKindPersistentDefine:
// will be removed by compactUnitMergeInfo
// but could be hit by other threads before
// that happens
do {
ix += 1 + sizeof(TypedValueAux) / sizeof(void*);
obj = mi->mergeableObj(ix);
k = UnitMergeKind(uintptr_t(obj) & 7);
} while (k == UnitMergeKindDefine);
continue;
case UnitMergeKindDefine:
do {
Stats::inc(Stats::UnitMerge_mergeable);
@@ -1219,11 +1332,9 @@ void Unit::mergeImpl(void* tcbase, UnitMergeInfo* mi) {
case UnitMergeKindDone:
Stats::inc(Stats::UnitMerge_mergeable, -1);
assert((unsigned)ix == mi->m_mergeablesSize);
if (UNLIKELY((m_mergeState & (UnitMergeStateUniqueClasses|
UnitMergeStateUniqueDefinedClasses)) ==
UnitMergeStateUniqueClasses)) {
if (UNLIKELY(m_mergeState & UnitMergeStateNeedsCompact)) {
SimpleLock lock(unitInitLock);
if (m_mergeState & UnitMergeStateUniqueDefinedClasses) return;
if (!(m_mergeState & UnitMergeStateNeedsCompact)) return;
/*
* All the classes are known to be unique, and we just got
* here, so all were successfully defined. We can now go
@@ -1255,7 +1366,7 @@ void Unit::mergeImpl(void* tcbase, UnitMergeInfo* mi) {
m_mergeState |= UnitMergeStateEmpty;
}
}
m_mergeState |= UnitMergeStateUniqueDefinedClasses;
m_mergeState &= ~UnitMergeStateNeedsCompact;
assert(newMi->m_firstMergeablePreClass == newMi->m_mergeablesSize ||
isMergeOnly());
}
@@ -1533,7 +1644,8 @@ Func* Unit::lookupFunc(const StringData* funcName) {
Func* Unit::loadFunc(const NamedEntity* ne, const StringData* funcName) {
Func* func = ne->getCachedFunc();
if (UNLIKELY(!func)) {
if (AutoloadHandler::s_instance->autoloadFunc(StrNR(funcName))) {
if (AutoloadHandler::s_instance->autoloadFunc(
const_cast<StringData*>(funcName))) {
func = ne->getCachedFunc();
}
}
@@ -1868,6 +1980,7 @@ void UnitRepoProxy::InsertUnitMergeableStmt
query.bindId("@mergeableId", id);
if (value) {
assert(kind == UnitMergeKindDefine ||
kind == UnitMergeKindPersistentDefine ||
kind == UnitMergeKindGlobal);
query.bindTypedValue("@mergeableValue", *value);
} else {
@@ -1912,6 +2025,7 @@ void UnitRepoProxy::GetUnitMergeablesStmt
ue.insertMergeableInclude(mergeableIx,
(UnitMergeKind)mergeableKind, mergeableId);
break;
case UnitMergeKindPersistentDefine:
case UnitMergeKindDefine:
case UnitMergeKindGlobal: {
TypedValue mergeableValue; /**/ query.getTypedValue(3,
@@ -2400,6 +2514,7 @@ bool UnitEmitter::insert(UnitOrigin unitOrigin, RepoTxn& txn) {
break;
}
case UnitMergeKindDefine:
case UnitMergeKindPersistentDefine:
case UnitMergeKindGlobal: {
int ix = m_mergeableStmts[i].second;
urp.insertUnitMergeable(repoId).insert(
@@ -2495,6 +2610,7 @@ Unit* UnitEmitter::create() {
break;
}
} else switch (it->first) {
case UnitMergeKindPersistentDefine:
case UnitMergeKindDefine:
case UnitMergeKindGlobal:
extra += sizeof(TypedValueAux) / sizeof(void*);
@@ -2544,6 +2660,7 @@ Unit* UnitEmitter::create() {
break;
}
case UnitMergeKindDefine:
case UnitMergeKindPersistentDefine:
case UnitMergeKindGlobal: {
assert(RuntimeOption::RepoAuthoritative);
void* name = u->lookupLitstrId(m_mergeableValues[it->second].first);
+19 -4
Ver Arquivo
@@ -50,9 +50,13 @@ enum UnitMergeKind {
// UnitMergeKindClass is required to be 0 for correctness.
UnitMergeKindClass = 0,
UnitMergeKindUniqueDefinedClass = 1,
// Top level, scalar defines in the unit
UnitMergeKindDefine = 2,
UnitMergeKindGlobal = 3,
// 4 and 5 are available
// Top level, scalar defines that will be loaded once
// and preserved from request to request
UnitMergeKindPersistentDefine = 3,
UnitMergeKindGlobal = 4,
// 5 is available
UnitMergeKindReqDoc = 6,
UnitMergeKindDone = 7,
// We cannot add more kinds here; this has to fit in 3 bits.
@@ -63,8 +67,7 @@ enum UnitMergeState {
UnitMergeStateMerging = 1,
UnitMergeStateMerged = 2,
UnitMergeStateUniqueFuncs = 4,
UnitMergeStateUniqueClasses = 8,
UnitMergeStateUniqueDefinedClasses = 16,
UnitMergeStateNeedsCompact = 8,
UnitMergeStateEmpty = 32
};
@@ -490,6 +493,18 @@ struct Unit {
bool failIsFatal = true);
void defTypedef(Id id);
static TypedValue* lookupCns(const StringData* cnsName);
static TypedValue* lookupPersistentCns(const StringData* cnsName);
static TypedValue* loadCns(const StringData* cnsName);
static bool defCns(const StringData* cnsName, const TypedValue* value,
bool persistent = false);
static uint64_t defCnsHelper(uint64_t ch,
const TypedValue* value,
const StringData* cnsName);
static void defDynamicSystemConstant(const StringData* cnsName,
const void* data);
static bool defCnsDynamic(const StringData* cnsName, TypedValue* value);
/*
* Find the Class* for a defined class corresponding to the name
* `clsName'.