Switch to much tighter memory layout

This commit is contained in:
Michal Moskal 2016-09-08 21:10:01 +01:00
Родитель d7819ff326
Коммит 53019799d1
3 изменённых файлов: 173 добавлений и 171 удалений

1
.gitignore поставляемый
Просмотреть файл

@ -2,7 +2,6 @@
build
*.swp
*.log
microbit-touchdevelop/MicroBitTouchDevelop.h.gch
test.cpp
yotta_modules
yotta_targets

192
inc/pxt.h
Просмотреть файл

@ -62,7 +62,6 @@ namespace pxt {
int templateHash();
int programHash();
int getNumGlobals();
RefRecord* mkRecord(int reflen, int totallen);
RefRecord* mkClassInstance(int vtableOffset);
// The standard calling convention is:
@ -96,28 +95,52 @@ namespace pxt {
}
#ifdef DEBUG_MEMLEAKS
class RefObject;
#ifdef DEBUG_MEMLEAKS
extern std::set<RefObject*> allptrs;
#endif
typedef void (*RefObjectMethod)(RefObject *self);
typedef void *PVoid;
typedef void **PPVoid;
const PPVoid RefMapMarker = (PPVoid)(void*)43;
struct VTable {
uint16_t numbytes; // in the entire object, including the vtable pointer
uint16_t userdata;
PVoid *ifaceTable;
PVoid methods[10];
// refmask sits at &methods[nummethods]
};
const int vtableShift = 2;
// A base abstract class for ref-counted objects.
class RefObject
{
public:
uint16_t refcnt;
uint16_t vtable;
RefObject()
RefObject(uint16_t vt)
{
refcnt = 1;
refcnt = 2;
vtable = vt;
#ifdef DEBUG_MEMLEAKS
allptrs.insert(this);
#endif
}
// Call to disable pointer tracking on the current instance.
void canLeak()
{
inline VTable *getVTable() {
return (VTable*)(vtable << vtableShift);
}
void destroy();
void print();
// Call to disable pointer tracking on the current instance (in destructor or some other hack)
inline void untrack() {
#ifdef DEBUG_MEMLEAKS
allptrs.erase(this);
#endif
@ -128,51 +151,17 @@ namespace pxt {
{
check(refcnt > 0, ERR_REF_DELETED);
//printf("INCR "); this->print();
refcnt++;
refcnt += 2;
}
inline void unref()
{
//printf("DECR "); this->print();
if (--refcnt == 0) {
delete this;
refcnt -= 2;
if (refcnt == 0) {
destroy();
}
}
virtual void print();
virtual ~RefObject()
{
// This is just a base class for ref-counted objects.
// There is nothing to free yet, but derived classes will have things to free.
#ifdef DEBUG_MEMLEAKS
allptrs.erase(this);
#endif
}
// This is used by indexOf function, overridden in RefString
virtual bool equals(RefObject *other)
{
return this == other;
}
};
// Ref-counted wrapper around any C++ object.
template <class T>
class RefStruct
: public RefObject
{
public:
T v;
virtual ~RefStruct() { }
virtual void print()
{
printf("RefStruct %p r=%d\n", this, refcnt);
}
RefStruct(const T& i) : v(i) {}
};
// A ref-counted collection of either primitive or ref-counted objects (String, Image,
@ -183,13 +172,13 @@ namespace pxt {
public:
// 1 - collection of refs (need decr)
// 2 - collection of strings (in fact we always have 3, never 2 alone)
uint16_t flags;
inline uint32_t getFlags() { return getVTable()->userdata; }
inline bool isRef() { return getFlags() & 1; }
inline bool isString() { return getFlags() & 2; }
std::vector<uint32_t> data;
RefCollection(uint16_t f) : flags(f) {}
virtual ~RefCollection();
virtual void print();
RefCollection(uint16_t f);
inline bool in_range(int x) {
return (0 <= x && x < (int)data.size());
@ -197,6 +186,9 @@ namespace pxt {
inline int length() { return data.size(); }
void destroy();
void print();
void push(uint32_t x);
uint32_t getAt(int x);
void removeAt(int x);
@ -214,49 +206,34 @@ namespace pxt {
: public RefObject
{
public:
uint16_t padding;
uint32_t vtable;
std::vector<MapEntry> data;
RefMap() : padding(0), vtable(42) {}
virtual ~RefMap();
virtual void print();
RefMap();
void destroy();
void print();
int findIdx(uint32_t key);
};
struct VTable {
uint16_t refcount; // 0xffff
uint8_t numfields;
uint8_t nummethods;
uint32_t methods[];
// refmask sits at &methods[nummethods]
};
// A ref-counted, user-defined Touch Develop object.
// A ref-counted, user-defined JS object.
class RefRecord
: public RefObject
{
public:
// Total number of fields.
uint8_t len;
// Number of fields which are ref-counted pointers; these always come first
// on the fields[] array.
uint8_t reflen;
// The object is allocated, so that there is space at the end for the fields.
uint32_t fields[];
virtual ~RefRecord();
virtual void print();
RefRecord(uint16_t v) : RefObject(v) {}
uint32_t ld(int idx);
uint32_t ldref(int idx);
void st(int idx, uint32_t v);
void stref(int idx, uint32_t v);
};
// these are needed when constructing vtables for user-defined classes
void RefRecord_destroy(RefRecord *r);
void RefRecord_print(RefRecord *r);
class RefAction;
typedef uint32_t (*ActionCB)(uint32_t *captured, uint32_t arg0, uint32_t arg1, uint32_t arg2);
@ -272,8 +249,10 @@ namespace pxt {
// fields[] contain captured locals
uint32_t fields[];
virtual ~RefAction();
virtual void print();
void destroy();
void print();
RefAction();
inline void stCore(int idx, uint32_t v)
{
@ -298,9 +277,9 @@ namespace pxt {
{
public:
uint32_t v;
virtual void print();
RefLocal() : v(0) {}
void destroy();
void print();
RefLocal();
};
class RefRefLocal
@ -308,36 +287,10 @@ namespace pxt {
{
public:
uint32_t v;
virtual void print();
virtual ~RefRefLocal();
RefRefLocal() : v(0) {}
void destroy();
void print();
RefRefLocal();
};
// A ref-counted byte buffer
class RefBuffer
: public RefObject
{
public:
std::vector<uint8_t> data;
virtual ~RefBuffer()
{
data.resize(0);
}
virtual void print()
{
printf("RefBuffer %p r=%d size=%d [%p, ...]\n", this, refcnt, data.size(), data.size() > 0 ? data[0] : 0);
}
char *cptr() { return (char*)&data[0]; }
int size() { return data.size(); }
};
}
// The ARM Thumb generator in the JavaScript code is parsing
@ -352,6 +305,29 @@ namespace pxt { \
#define PXT_SHIMS_END }; }
#pragma GCC diagnostic ignored "-Wpmf-conversions"
#define PXT_VTABLE_TO_INT(vt) ((uint32_t)(vt) >> vtableShift)
#define PXT_VTABLE_BEGIN(classname, flags, iface) \
const VTable classname ## _vtable \
__attribute__((aligned(1 << vtableShift))) \
= { \
sizeof(classname), \
flags, \
iface, \
{ \
(void*)&classname::destroy, \
(void*)&classname::print,
#define PXT_VTABLE_END } };
#define PXT_VTABLE_INIT(classname) \
RefObject(PXT_VTABLE_TO_INT(&classname ## _vtable))
#define PXT_VTABLE_CTOR(classname) \
PXT_VTABLE_BEGIN(classname, 0, 0) PXT_VTABLE_END \
classname::classname() : PXT_VTABLE_INIT(classname)
#endif
// vim: ts=2 sw=2 expandtab

Просмотреть файл

@ -73,43 +73,29 @@ namespace pxt {
return runAction3(a, 0, 0, 0);
}
RefRecord* mkRecord(int reflen, int totallen)
{
intcheck(0 <= reflen && reflen <= totallen, ERR_SIZE, 1);
intcheck(reflen <= totallen && totallen < 255, ERR_SIZE, 2);
void *ptr = ::operator new(sizeof(RefRecord) + totallen * sizeof(uint32_t));
RefRecord *r = new (ptr) RefRecord();
r->len = totallen;
r->reflen = reflen;
memset(r->fields, 0, r->len * sizeof(uint32_t));
return r;
}
RefRecord* mkClassInstance(int vtableOffset)
{
VTable *vtable = (VTable*)&bytecode[vtableOffset];
intcheck(vtable->refcount == 0xffff, ERR_SIZE, 3);
intcheck(vtable->methods[0] == &RefRecord_destroy, ERR_SIZE, 3);
intcheck(vtable->methods[1] == &RefRecord_print, ERR_SIZE, 4);
void *ptr = ::operator new(sizeof(RefRecord) + vtable->numfields * sizeof(uint32_t));
RefRecord *r = new (ptr) RefRecord();
r->len = vtable->numfields;
r->reflen = 255;
memset(r->fields, 0, r->len * sizeof(uint32_t));
r->fields[0] = (uint32_t)vtable;
void *ptr = ::operator new(vtable->numbytes);
RefRecord *r = new (ptr) RefRecord(PXT_VTABLE_TO_INT(vtable));
memset(r->fields, 0, vtable->numbytes - sizeof(RefRecord));
return r;
}
uint32_t RefRecord::ld(int idx)
{
intcheck((reflen == 255 ? 0 : reflen) <= idx && idx < len, ERR_OUT_OF_BOUNDS, 1);
//intcheck((reflen == 255 ? 0 : reflen) <= idx && idx < len, ERR_OUT_OF_BOUNDS, 1);
return fields[idx];
}
uint32_t RefRecord::ldref(int idx)
{
//printf("LD %p len=%d reflen=%d idx=%d\n", this, len, reflen, idx);
intcheck(0 <= idx && idx < reflen, ERR_OUT_OF_BOUNDS, 2);
//intcheck(0 <= idx && idx < reflen, ERR_OUT_OF_BOUNDS, 2);
uint32_t tmp = fields[idx];
incr(tmp);
return tmp;
@ -117,52 +103,51 @@ namespace pxt {
void RefRecord::st(int idx, uint32_t v)
{
intcheck((reflen == 255 ? 0 : reflen) <= idx && idx < len, ERR_OUT_OF_BOUNDS, 3);
//intcheck((reflen == 255 ? 0 : reflen) <= idx && idx < len, ERR_OUT_OF_BOUNDS, 3);
fields[idx] = v;
}
void RefRecord::stref(int idx, uint32_t v)
{
//printf("ST %p len=%d reflen=%d idx=%d\n", this, len, reflen, idx);
intcheck(0 <= idx && idx < reflen, ERR_OUT_OF_BOUNDS, 4);
//intcheck(0 <= idx && idx < reflen, ERR_OUT_OF_BOUNDS, 4);
decr(fields[idx]);
fields[idx] = v;
}
RefRecord::~RefRecord()
{
if (reflen == 255) {
// assuming vtable
VTable *tbl = (VTable*)fields[0];
uint8_t *refmask = (uint8_t*)&tbl->methods[tbl->nummethods];
int len = tbl->numfields;
void RefObject::destroy() {
((RefObjectMethod)getVTable()->methods[0])(this);
delete this;
}
void RefObject::print() {
((RefObjectMethod)getVTable()->methods[1])(this);
}
void RefRecord_destroy(RefRecord *r) {
auto tbl = r->getVTable();
uint8_t *refmask = (uint8_t*)&tbl->methods[tbl->userdata & 0xff];
int len = (tbl->numbytes >> 2) - 1;
for (int i = 0; i < len; ++i) {
if (refmask[i]) decr(fields[i]);
fields[i] = 0;
if (refmask[i]) decr(r->fields[i]);
r->fields[i] = 0;
}
} else {
for (int i = 0; i < this->reflen; ++i) {
decr(fields[i]);
fields[i] = 0;
}
}
}
void RefRecord::print()
void RefRecord_print(RefRecord *r)
{
printf("RefRecord %p r=%d size=%d (%d refs)\n", this, refcnt, len, reflen);
printf("RefRecord %p r=%d size=%d bytes\n", r, r->refcnt, r->getVTable()->numbytes);
}
void RefCollection::push(uint32_t x) {
if (flags & 1) incr(x);
if (isRef()) incr(x);
data.push_back(x);
}
uint32_t RefCollection::getAt(int x) {
if (in_range(x)) {
uint32_t tmp = data.at(x);
if (flags & 1) incr(tmp);
if (isRef()) incr(tmp);
return tmp;
}
else {
@ -175,7 +160,7 @@ namespace pxt {
if (!in_range(x))
return;
if (flags & 1) decr(data.at(x));
if (isRef()) decr(data.at(x));
data.erase(data.begin()+x);
}
@ -183,7 +168,7 @@ namespace pxt {
if (!in_range(x))
return;
if (flags & 1) {
if (isRef()) {
decr(data.at(x));
incr(y);
}
@ -194,7 +179,7 @@ namespace pxt {
if (!in_range(start))
return -1;
if (flags & 2) {
if (isString()) {
StringData *xx = (StringData*)x;
for (uint32_t i = start; i < data.size(); ++i) {
StringData *ee = (StringData*)data.at(i);
@ -219,29 +204,55 @@ namespace pxt {
return 0;
}
void RefObject::print()
{
printf("RefObject %p\n", this);
namespace Coll0 {
PXT_VTABLE_BEGIN(RefCollection, 0, 0)
PXT_VTABLE_END
}
namespace Coll1 {
PXT_VTABLE_BEGIN(RefCollection, 1, 0)
PXT_VTABLE_END
}
namespace Coll3 {
PXT_VTABLE_BEGIN(RefCollection, 3, 0)
PXT_VTABLE_END
}
RefCollection::~RefCollection()
RefCollection::RefCollection(uint16_t flags) : RefObject(0) {
switch (flags) {
case 0:
vtable = PXT_VTABLE_TO_INT(&Coll0::RefCollection_vtable);
break;
case 1:
vtable = PXT_VTABLE_TO_INT(&Coll1::RefCollection_vtable);
break;
case 3:
vtable = PXT_VTABLE_TO_INT(&Coll3::RefCollection_vtable);
break;
default:
error(ERR_SIZE);
break;
}
}
void RefCollection::destroy()
{
// printf("KILL "); this->print();
if (flags & 1)
for (uint32_t i = 0; i < data.size(); ++i) {
decr(data[i]);
data[i] = 0;
if (this->isRef())
for (uint32_t i = 0; i < this->data.size(); ++i) {
decr(this->data[i]);
this->data[i] = 0;
}
data.resize(0);
this->data.resize(0);
}
void RefCollection::print()
{
printf("RefCollection %p r=%d flags=%d size=%d [%p, ...]\n", this, refcnt, flags, data.size(), data.size() > 0 ? data[0] : 0);
printf("RefCollection %p r=%d flags=%d size=%d [%p, ...]\n", this, refcnt, getFlags(), data.size(), data.size() > 0 ? data[0] : 0);
}
PXT_VTABLE_CTOR(RefAction) {}
// fields[] contain captured locals
RefAction::~RefAction()
void RefAction::destroy()
{
for (int i = 0; i < this->reflen; ++i) {
decr(fields[i]);
@ -259,17 +270,33 @@ namespace pxt {
printf("RefLocal %p r=%d v=%d\n", this, refcnt, v);
}
void RefLocal::destroy()
{
}
PXT_VTABLE_CTOR(RefLocal) {
v = 0;
}
PXT_VTABLE_CTOR(RefRefLocal) {
v = 0;
}
void RefRefLocal::print()
{
printf("RefRefLocal %p r=%d v=%p\n", this, refcnt, (void*)v);
}
RefRefLocal::~RefRefLocal()
void RefRefLocal::destroy()
{
decr(v);
}
RefMap::~RefMap() {
PXT_VTABLE_BEGIN(RefMap, 0, RefMapMarker)
PXT_VTABLE_END
RefMap::RefMap() : PXT_VTABLE_INIT(RefMap) {}
void RefMap::destroy() {
for (unsigned i = 0; i < data.size(); ++i) {
if (data[i].key & 1) {
decr(data[i].val);
@ -430,7 +457,7 @@ namespace pxt {
((uint32_t (*)())startptr)();
#ifdef DEBUG_MEMLEAKS
bitvm::debugMemLeaks();
pxt::debugMemLeaks();
#endif
return;