Arquivos
hhvm/hphp/runtime/base/shared/shared_variant.h
T
smith 22058efe41 Allow different RefData and TypedValue layouts
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.
2013-04-01 11:51:31 -07:00

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__ */