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:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -200,6 +200,7 @@ void ProcessInit() {
|
||||
*(info->m_clsPtr) = cls;
|
||||
}
|
||||
|
||||
ClassInfo::InitializeSystemConstants();
|
||||
Stack::ValidateStackSize();
|
||||
SystemLib::s_inited = true;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) \
|
||||
/* */ \
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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'.
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário