22058efe41
Fixing various bugs all over the VM that make assumptions about RefData and TypedValue layout. Here are the assumptions fixed by this diff: offsetof(RefData, m_tv) == 0. Both JIT's assumed this in many subtle ways, by punning RefData* as TypedValue* without adding an offset. This assumption also causes RefData._count to overlap TypedValue.m_aux, which constraints TypedValue layout. offsetof(TypedValue, m_data) == 0. gen_ext_hhvm.php assumes you can cast TypedValue* to Value*; the JITs often weren't using offsetof(TypedValue, m_data) in their addressing calculations. HHIR assumed return-by-value TV's have m_data/m_type in rax/rdx, which can change when TV layout changes. offsetof(TypedValue, m_type) > 8 is an assumption baked into the pass-by-value register assignment logic in HHIR's codegen.cpp; if the type is in the low word, register assignment is swapped. sizeof(TypedValue::m_type) == 4. We used dword-sized operations in both JIT's when accessing m_type. Now, we use helper functions that are sensitive to sizeof(DataType) Configuration: DEBUG=: (opt) same layouts as trunk for RefData & TypedValue DEBUG=1: (dbg) new RefData layout (m_tv doesn't overlap RefData::_count) PACKED_TV=1, DEBUG=*: new RefData and TypedValue layout.
276 linhas
7.9 KiB
C++
276 linhas
7.9 KiB
C++
/*
|
|
+----------------------------------------------------------------------+
|
|
| HipHop for PHP |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
|
|
+----------------------------------------------------------------------+
|
|
| This source file is subject to version 3.01 of the PHP license, |
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
| available through the world-wide-web at the following url: |
|
|
| http://www.php.net/license/3_01.txt |
|
|
| If you did not receive a copy of the PHP license and are unable to |
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
| license@php.net so we can mail you a copy immediately. |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
#ifndef __HPHP_SHARED_VARIANT_H__
|
|
#define __HPHP_SHARED_VARIANT_H__
|
|
|
|
#include <runtime/base/types.h>
|
|
#include <util/lock.h>
|
|
#include <util/hash.h>
|
|
#include <util/atomic.h>
|
|
#include <runtime/base/complex_types.h>
|
|
#include <runtime/base/shared/immutable_map.h>
|
|
#include <runtime/base/shared/immutable_obj.h>
|
|
|
|
#if (defined(__APPLE__) || defined(__APPLE_CC__)) && (defined(__BIG_ENDIAN__) || defined(__LITTLE_ENDIAN__))
|
|
# if defined(__LITTLE_ENDIAN__)
|
|
# undef WORDS_BIGENDIAN
|
|
# else
|
|
# if defined(__BIG_ENDIAN__)
|
|
# define WORDS_BIGENDIAN
|
|
# endif
|
|
# endif
|
|
#endif
|
|
|
|
namespace HPHP {
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
class SharedMap;
|
|
|
|
class SharedVariantStats;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
class SharedVariant {
|
|
public:
|
|
SharedVariant(CVarRef source, bool serialized, bool inner = false,
|
|
bool unserializeObj = false);
|
|
~SharedVariant();
|
|
|
|
// Create will do the wrapped check before creating a SharedVariant
|
|
static SharedVariant* Create(CVarRef source, bool serialized,
|
|
bool inner = false,
|
|
bool unserializeObj = false);
|
|
|
|
bool is(DataType d) const { return m_type == d; }
|
|
DataType getType() const { return (DataType)m_type; }
|
|
CVarRef asCVarRef() const {
|
|
// Must be non-refcounted types
|
|
assert(m_shouldCache == false);
|
|
assert(m_flags == 0);
|
|
assert(!IS_REFCOUNTED_TYPE(m_tv.m_type));
|
|
return tvAsCVarRef(&m_tv);
|
|
}
|
|
|
|
void incRef() {
|
|
atomic_inc(m_count);
|
|
}
|
|
|
|
void decRef() {
|
|
assert(m_count);
|
|
if (atomic_dec(m_count) == 0) {
|
|
delete this;
|
|
}
|
|
}
|
|
|
|
Variant toLocal();
|
|
|
|
int64_t intData() const {
|
|
assert(is(KindOfInt64));
|
|
return m_data.num;
|
|
}
|
|
|
|
const char *stringData() const {
|
|
assert(is(KindOfString) || is(KindOfStaticString));
|
|
return m_data.str->data();
|
|
}
|
|
|
|
size_t stringLength() const {
|
|
assert(is(KindOfString) || is(KindOfStaticString));
|
|
return m_data.str->size();
|
|
}
|
|
|
|
strhash_t stringHash() const {
|
|
assert(is(KindOfString) || is(KindOfStaticString));
|
|
return m_data.str->hash();
|
|
}
|
|
|
|
size_t arrSize() const {
|
|
assert(is(KindOfArray));
|
|
if (getIsVector()) return m_data.vec->m_size;
|
|
return m_data.map->size();
|
|
}
|
|
|
|
size_t arrCap() const {
|
|
assert(is(KindOfArray));
|
|
if (getIsVector()) return m_data.vec->m_size;
|
|
return m_data.map->capacity();
|
|
}
|
|
|
|
int getIndex(int64_t key);
|
|
int getIndex(const StringData* key);
|
|
|
|
void loadElems(ArrayData *&elems, const SharedMap &sharedMap,
|
|
bool mapInit = false);
|
|
|
|
Variant getKey(ssize_t pos) const;
|
|
|
|
SharedVariant* getValue(ssize_t pos) const;
|
|
|
|
// implementing LeakDetectable
|
|
void dump(std::string &out);
|
|
|
|
void getStats(SharedVariantStats *stats) const;
|
|
int32_t getSpaceUsage() const;
|
|
|
|
StringData *getStringData() const {
|
|
assert(is(KindOfString) || is(KindOfStaticString));
|
|
return m_data.str;
|
|
}
|
|
|
|
SharedVariant *convertObj(CVarRef var);
|
|
bool isUnserializedObj() { return getIsObj(); }
|
|
bool shouldCache() const { return m_shouldCache; }
|
|
|
|
int countReachable() const;
|
|
|
|
private:
|
|
class VectorData {
|
|
public:
|
|
union {
|
|
size_t m_size;
|
|
SharedVariant* m_align_dummy;
|
|
};
|
|
|
|
VectorData() : m_size(0) {}
|
|
|
|
~VectorData() {
|
|
SharedVariant** v = vals();
|
|
for (size_t i = 0; i < m_size; i++) {
|
|
v[i]->decRef();
|
|
}
|
|
}
|
|
SharedVariant** vals() { return (SharedVariant**)(this + 1); }
|
|
void *operator new(size_t sz, int num) {
|
|
assert(sz == sizeof(VectorData));
|
|
return malloc(sizeof(VectorData) + num * sizeof(SharedVariant*));
|
|
}
|
|
void operator delete(void* ptr) { free(ptr); }
|
|
// just to keep the compiler happy; used if the constructor throws
|
|
void operator delete(void* ptr, int num) { free(ptr); }
|
|
};
|
|
|
|
/*
|
|
* Keep the object layout binary compatible with Variant for primitive types.
|
|
* We want to have compile time assertion to guard it but still want to have
|
|
* anonymous struct. For non-refcounted types, m_shouldCache and m_flags are
|
|
* guaranteed to be 0, and other parts of runtime will not touch the count.
|
|
*/
|
|
|
|
union SharedData {
|
|
int64_t num;
|
|
double dbl;
|
|
StringData *str;
|
|
ImmutableMap* map;
|
|
VectorData* vec;
|
|
ImmutableObj* obj;
|
|
};
|
|
|
|
union {
|
|
TypedValue m_tv;
|
|
struct {
|
|
#if PACKED_TV
|
|
uint8_t _typePad;
|
|
DataType m_type;
|
|
bool m_shouldCache;
|
|
uint8_t m_flags;
|
|
uint32_t m_count;
|
|
SharedData m_data;
|
|
#else
|
|
#ifdef WORDS_BIGENDIAN
|
|
SharedData m_data;
|
|
uint32_t m_count;
|
|
bool m_shouldCache;
|
|
uint8_t m_flags;
|
|
uint16_t m_type;
|
|
#else
|
|
SharedData m_data;
|
|
uint32_t m_count;
|
|
uint16_t m_type;
|
|
bool m_shouldCache;
|
|
uint8_t m_flags;
|
|
#endif
|
|
#endif
|
|
};
|
|
};
|
|
|
|
const static uint8_t SerializedArray = (1<<0);
|
|
const static uint8_t IsVector = (1<<1);
|
|
const static uint8_t IsObj = (1<<2);
|
|
const static uint8_t ObjAttempted = (1<<3);
|
|
|
|
static void compileTimeAssertions() {
|
|
static_assert(offsetof(SharedVariant, m_data) == offsetof(TypedValue, m_data),
|
|
"Offset of m_data must be equal in SharedVar and TypedValue");
|
|
static_assert(offsetof(SharedVariant, m_count) == TypedValueAux::auxOffset,
|
|
"Offset of m_count must equal offset of TV.m_aux");
|
|
static_assert(offsetof(SharedVariant, m_type) == offsetof(TypedValue, m_type),
|
|
"Offset of m_type must be equal in SharedVar and TypedValue");
|
|
static_assert(sizeof(SharedVariant) == sizeof(TypedValue),
|
|
"Be careful with field layout");
|
|
}
|
|
|
|
bool getSerializedArray() const { return (bool)(m_flags & SerializedArray);}
|
|
void setSerializedArray() { m_flags |= SerializedArray;}
|
|
void clearSerializedArray() { m_flags &= ~SerializedArray;}
|
|
|
|
bool getIsVector() const { return (bool)(m_flags & IsVector);}
|
|
void setIsVector() { m_flags |= IsVector;}
|
|
void clearIsVector() { m_flags &= ~IsVector;}
|
|
|
|
bool getIsObj() const { return (bool)(m_flags & IsObj);}
|
|
void setIsObj() { m_flags |= IsObj;}
|
|
void clearIsObj() { m_flags &= ~IsObj;}
|
|
|
|
bool getObjAttempted() const { return (bool)(m_flags & ObjAttempted);}
|
|
void setObjAttempted() { m_flags |= ObjAttempted;}
|
|
void clearObjAttempted() { m_flags &= ~ObjAttempted;}
|
|
};
|
|
|
|
class SharedVariantStats {
|
|
public:
|
|
int32_t dataSize;
|
|
int32_t dataTotalSize;
|
|
int32_t variantCount;
|
|
|
|
void initStats() {
|
|
variantCount = 0;
|
|
dataSize = 0;
|
|
dataTotalSize = 0;
|
|
}
|
|
|
|
SharedVariantStats() {
|
|
initStats();
|
|
}
|
|
|
|
void addChildStats(const SharedVariantStats *childStats) {
|
|
dataSize += childStats->dataSize;
|
|
dataTotalSize += childStats->dataTotalSize;
|
|
variantCount += childStats->variantCount;
|
|
}
|
|
|
|
void removeChildStats(const SharedVariantStats *childStats) {
|
|
dataSize -= childStats->dataSize;
|
|
dataTotalSize -= childStats->dataTotalSize;
|
|
variantCount -= childStats->variantCount;
|
|
}
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
}
|
|
|
|
#endif /* __HPHP_SHARED_VARIANT_H__ */
|