Commit 933834be authored by Kevin Modzelewski's avatar Kevin Modzelewski

Merge pull request #510 from kmod/singleton_hcls

Singleton hcls storage strategy
parents 9030ec59 4ca839f9
......@@ -2717,13 +2717,14 @@ extern "C" int PyType_Ready(PyTypeObject* cls) noexcept {
RELEASE_ASSERT(cls->tp_del == NULL, "");
RELEASE_ASSERT(cls->tp_version_tag == 0, "");
// I think it is safe to ignore these for for now:
// RELEASE_ASSERT(cls->tp_weaklistoffset == 0, "");
// RELEASE_ASSERT(cls->tp_traverse == NULL, "");
// RELEASE_ASSERT(cls->tp_clear == NULL, "");
// I think it is safe to ignore these for for now:
// RELEASE_ASSERT(cls->tp_weaklistoffset == 0, "");
// RELEASE_ASSERT(cls->tp_traverse == NULL, "");
// RELEASE_ASSERT(cls->tp_clear == NULL, "");
assert(cls->attrs.hcls == NULL);
new (&cls->attrs) HCAttrs(HiddenClass::makeSingleton());
#define INITIALIZE(a) new (&(a)) decltype(a)
INITIALIZE(cls->attrs);
INITIALIZE(cls->dependent_icgetattrs);
#undef INITIALIZE
......
......@@ -418,7 +418,7 @@ public:
HiddenClass* hcls;
AttrList* attr_list;
HCAttrs() : hcls(root_hcls), attr_list(nullptr) {}
HCAttrs(HiddenClass* hcls = root_hcls) : hcls(hcls), attr_list(nullptr) {}
};
class BoxedDict;
......@@ -428,11 +428,8 @@ class Box {
private:
BoxedDict** getDictPtr();
// Adds a new attribute to a HCAttrs-backed object. Must pass in the new hidden class object
// which must be the same as the current hidden class but with the new attribute at the end.
// Swaps the hidden class, reallocates and copies and updates the attribute array.
// The value of the current hidden class should be guarded before calling this.
void addNewHCAttr(HiddenClass* new_hcls, Box* val, SetattrRewriteArgs* rewrite_args);
// Appends a new value to the hcattrs array.
void appendNewHCAttr(Box* val, SetattrRewriteArgs* rewrite_args);
public:
// Add a no-op constructor to make sure that we don't zero-initialize cls
......
......@@ -208,7 +208,7 @@ struct HeapStatistics {
TypeStats total;
HeapStatistics(bool collect_cls_stats, bool collect_hcls_stats)
: collect_cls_stats(collect_cls_stats), collect_hcls_stats(collect_hcls_stats) {
: collect_cls_stats(collect_cls_stats), collect_hcls_stats(collect_hcls_stats), num_hcls_by_attrs_exceed(0) {
memset(num_hcls_by_attrs, 0, sizeof(num_hcls_by_attrs));
}
};
......@@ -233,6 +233,10 @@ void addStatistic(HeapStatistics* stats, GCAllocation* al, int nbytes) {
Box* b = (Box*)al->user_data;
if (b->cls->instancesHaveHCAttrs()) {
HCAttrs* attrs = b->getHCAttrsPtr();
if (attrs->hcls->attributeArraySize() >= 20) {
printf("%s object has %d attributes\n", b->cls->tp_name, attrs->hcls->attributeArraySize());
}
stats->hcls_uses[attrs->hcls]++;
}
}
......
......@@ -228,7 +228,6 @@ extern "C" int PyDict_SetItem(PyObject* mp, PyObject* _key, PyObject* _item) noe
Box* item = static_cast<Box*>(_item);
try {
// TODO should demote GIL?
setitem(b, key, item);
} catch (ExcInfo e) {
abort();
......@@ -249,6 +248,16 @@ extern "C" int PyDict_SetItemString(PyObject* mp, const char* key, PyObject* ite
extern "C" PyObject* PyDict_GetItem(PyObject* dict, PyObject* key) noexcept {
ASSERT(isSubclass(dict->cls, dict_cls) || dict->cls == attrwrapper_cls, "%s", getTypeName(dict));
if (isSubclass(dict->cls, dict_cls)) {
BoxedDict* d = static_cast<BoxedDict*>(dict);
auto it = d->d.find(key);
if (it != d->d.end())
return it->second;
return NULL;
}
// This path doesn't exist in CPython; we have it to support extension modules that do
// something along the lines of PyDict_GetItem(PyModule_GetDict()):
try {
return getitem(dict, key);
} catch (ExcInfo e) {
......
......@@ -318,8 +318,8 @@ void BoxedClass::freeze() {
BoxedClass::BoxedClass(BoxedClass* base, gcvisit_func gc_visit, int attrs_offset, int weaklist_offset,
int instance_size, bool is_user_defined)
: BoxVar(0), gc_visit(gc_visit), simple_destructor(NULL), attrs_offset(attrs_offset), is_constant(false),
is_user_defined(is_user_defined), is_pyston_class(true) {
: BoxVar(0), attrs(HiddenClass::makeSingleton()), gc_visit(gc_visit), simple_destructor(NULL),
attrs_offset(attrs_offset), is_constant(false), is_user_defined(is_user_defined), is_pyston_class(true) {
// Zero out the CPython tp_* slots:
memset(&tp_name, 0, (char*)(&tp_version_tag + 1) - (char*)(&tp_name));
......@@ -481,6 +481,43 @@ const char* getNameOfClass(BoxedClass* cls) {
return cls->tp_name;
}
void HiddenClass::appendAttribute(llvm::StringRef attr) {
assert(type == SINGLETON);
dependent_getattrs.invalidateAll();
assert(attr_offsets.count(attr) == 0);
int n = this->attributeArraySize();
attr_offsets[attr] = n;
}
void HiddenClass::appendAttrwrapper() {
assert(type == SINGLETON);
dependent_getattrs.invalidateAll();
assert(attrwrapper_offset == -1);
attrwrapper_offset = this->attributeArraySize();
}
void HiddenClass::delAttribute(llvm::StringRef attr) {
assert(type == SINGLETON);
dependent_getattrs.invalidateAll();
assert(attr_offsets.count(attr));
int prev_idx = attr_offsets[attr];
attr_offsets.erase(attr);
for (auto it = attr_offsets.begin(), end = attr_offsets.end(); it != end; ++it) {
assert(it->second != prev_idx);
if (it->second > prev_idx)
it->second--;
}
if (attrwrapper_offset != -1 && attrwrapper_offset > prev_idx)
attrwrapper_offset--;
}
void HiddenClass::addDependence(Rewriter* rewriter) {
assert(type == SINGLETON);
rewriter->addDependenceOn(dependent_getattrs);
}
HiddenClass* HiddenClass::getOrMakeChild(const std::string& attr) {
STAT_TIMER(t0, "us_timer_hiddenclass_getOrMakeChild");
assert(type == NORMAL);
......@@ -629,14 +666,16 @@ Box* Box::getattr(const std::string& attr, GetattrRewriteArgs* rewrite_args) {
return r;
}
assert(hcls->type == HiddenClass::NORMAL);
if (rewrite_args)
rewrite_args->out_success = true;
assert(hcls->type == HiddenClass::NORMAL || hcls->type == HiddenClass::SINGLETON);
if (rewrite_args) {
if (!rewrite_args->obj_hcls_guarded)
if (!rewrite_args->obj_hcls_guarded) {
rewrite_args->obj->addAttrGuard(cls->attrs_offset + HCATTRS_HCLS_OFFSET, (intptr_t)hcls);
if (hcls->type == HiddenClass::SINGLETON)
hcls->addDependence(rewrite_args->rewriter);
}
rewrite_args->out_success = true;
}
int offset = hcls->getOffset(attr);
......@@ -645,8 +684,6 @@ Box* Box::getattr(const std::string& attr, GetattrRewriteArgs* rewrite_args) {
}
if (rewrite_args) {
// TODO using the output register as the temporary makes register allocation easier
// since we don't need to clobber a register, but does it make the code slower?
RewriterVar* r_attrs
= rewrite_args->obj->getAttr(cls->attrs_offset + HCATTRS_ATTRS_OFFSET, Location::any());
rewrite_args->out_rtn = r_attrs->getAttr(offset * sizeof(Box*) + ATTRLIST_ATTRS_OFFSET, Location::any());
......@@ -676,20 +713,12 @@ Box* Box::getattr(const std::string& attr, GetattrRewriteArgs* rewrite_args) {
return NULL;
}
void Box::addNewHCAttr(HiddenClass* new_hcls, Box* new_attr, SetattrRewriteArgs* rewrite_args) {
void Box::appendNewHCAttr(Box* new_attr, SetattrRewriteArgs* rewrite_args) {
assert(cls->instancesHaveHCAttrs());
HCAttrs* attrs = getHCAttrsPtr();
HiddenClass* hcls = attrs->hcls;
#ifndef NDEBUG
// make sure we don't need to rearrange the attributes
assert(new_hcls->attributeArraySize() == hcls->attributeArraySize() + 1);
for (const auto& p : hcls->getStrAttrOffsets()) {
assert(new_hcls->getStrAttrOffsets().lookup(p.first()) == p.second);
}
if (hcls->getAttrwrapperOffset() != -1)
assert(hcls->getAttrwrapperOffset() == new_hcls->getAttrwrapperOffset());
#endif
assert(hcls->type == HiddenClass::NORMAL || hcls->type == HiddenClass::SINGLETON);
int numattrs = hcls->attributeArraySize();
......@@ -711,18 +740,11 @@ void Box::addNewHCAttr(HiddenClass* new_hcls, Box* new_attr, SetattrRewriteArgs*
r_new_array2 = rewrite_args->rewriter->call(true, (void*)gc::gc_realloc, r_oldarray, r_newsize);
}
}
// Don't set the new hcls until after we do the allocation for the new attr_list;
// that allocation can cause a collection, and we want the collector to always
// see a consistent state between the hcls and the attr_list
attrs->hcls = new_hcls;
if (rewrite_args) {
r_new_array2->setAttr(numattrs * sizeof(Box*) + ATTRLIST_ATTRS_OFFSET, rewrite_args->attrval);
rewrite_args->obj->setAttr(cls->attrs_offset + HCATTRS_ATTRS_OFFSET, r_new_array2);
RewriterVar* r_hcls = rewrite_args->rewriter->loadConst((intptr_t)new_hcls);
rewrite_args->obj->setAttr(cls->attrs_offset + HCATTRS_HCLS_OFFSET, r_hcls);
rewrite_args->out_success = true;
}
attrs->attr_list->attrs[numattrs] = new_attr;
......@@ -760,13 +782,14 @@ void Box::setattr(const std::string& attr, Box* val, SetattrRewriteArgs* rewrite
return;
}
assert(hcls->type == HiddenClass::NORMAL);
assert(hcls->type == HiddenClass::NORMAL || hcls->type == HiddenClass::SINGLETON);
int offset = hcls->getOffset(attr);
if (rewrite_args) {
rewrite_args->obj->addAttrGuard(cls->attrs_offset + HCATTRS_HCLS_OFFSET, (intptr_t)hcls);
// rewrite_args->rewriter->addDecision(offset == -1 ? 1 : 0);
if (hcls->type == HiddenClass::SINGLETON)
hcls->addDependence(rewrite_args->rewriter);
}
if (offset >= 0) {
......@@ -788,12 +811,33 @@ void Box::setattr(const std::string& attr, Box* val, SetattrRewriteArgs* rewrite
}
assert(offset == -1);
HiddenClass* new_hcls = hcls->getOrMakeChild(attr);
// make sure we don't need to rearrange the attributes
assert(new_hcls->getStrAttrOffsets().lookup(attr) == hcls->attributeArraySize());
if (hcls->type == HiddenClass::NORMAL) {
HiddenClass* new_hcls = hcls->getOrMakeChild(attr);
// make sure we don't need to rearrange the attributes
assert(new_hcls->getStrAttrOffsets().lookup(attr) == hcls->attributeArraySize());
addNewHCAttr(new_hcls, val, rewrite_args);
this->appendNewHCAttr(val, rewrite_args);
attrs->hcls = new_hcls;
if (rewrite_args) {
if (!rewrite_args->out_success) {
rewrite_args = NULL;
} else {
RewriterVar* r_hcls = rewrite_args->rewriter->loadConst((intptr_t)new_hcls);
rewrite_args->obj->setAttr(cls->attrs_offset + HCATTRS_HCLS_OFFSET, r_hcls);
rewrite_args->out_success = true;
}
}
} else {
assert(hcls->type == HiddenClass::SINGLETON);
assert(!rewrite_args || !rewrite_args->out_success);
rewrite_args = NULL;
this->appendNewHCAttr(val, NULL);
hcls->appendAttribute(attr);
}
return;
}
......@@ -3825,8 +3869,7 @@ void Box::delattr(const std::string& attr, DelattrRewriteArgs* rewrite_args) {
return;
}
assert(hcls->type == HiddenClass::NORMAL);
HiddenClass* new_hcls = hcls->delAttrToMakeHC(attr);
assert(hcls->type == HiddenClass::NORMAL || hcls->type == HiddenClass::SINGLETON);
// The order of attributes is pertained as delAttrToMakeHC constructs
// the new HiddenClass by invoking getOrMakeChild in the prevous order
......@@ -3837,7 +3880,13 @@ void Box::delattr(const std::string& attr, DelattrRewriteArgs* rewrite_args) {
Box** start = attrs->attr_list->attrs;
memmove(start + offset, start + offset + 1, (num_attrs - offset - 1) * sizeof(Box*));
attrs->hcls = new_hcls;
if (hcls->type == HiddenClass::NORMAL) {
HiddenClass* new_hcls = hcls->delAttrToMakeHC(attr);
attrs->hcls = new_hcls;
} else {
assert(hcls->type == HiddenClass::SINGLETON);
hcls->delAttribute(attr);
}
// guarantee the size of the attr_list equals the number of attrs
int new_size = sizeof(HCAttrs::AttrList) + sizeof(Box*) * (num_attrs - 1);
......
......@@ -383,7 +383,8 @@ static void functionDtor(Box* b) {
}
// TODO(kmod): builtin modules are not supposed to have a __file__ attribute
BoxedModule::BoxedModule(const std::string& name, const std::string& fn, const char* doc) {
BoxedModule::BoxedModule(const std::string& name, const std::string& fn, const char* doc)
: attrs(HiddenClass::makeSingleton()) {
this->giveAttr("__name__", boxString(name));
this->giveAttr("__file__", boxString(fn));
this->giveAttr("__doc__", doc ? boxStrConstant(doc) : None);
......@@ -1255,7 +1256,8 @@ public:
// This check doesn't cover all cases, since an attrwrapper could be created around
// a normal object which then becomes dict-backed, so we RELEASE_ASSERT later
// that that doesn't happen.
assert(b->getHCAttrsPtr()->hcls->type == HiddenClass::NORMAL);
assert(b->getHCAttrsPtr()->hcls->type == HiddenClass::NORMAL
|| b->getHCAttrsPtr()->hcls->type == HiddenClass::SINGLETON);
}
DEFAULT_CLASS(attrwrapper_cls);
......@@ -1374,7 +1376,7 @@ public:
os << "attrwrapper({";
HCAttrs* attrs = self->b->getHCAttrsPtr();
RELEASE_ASSERT(attrs->hcls->type == HiddenClass::NORMAL, "");
RELEASE_ASSERT(attrs->hcls->type == HiddenClass::NORMAL || attrs->hcls->type == HiddenClass::SINGLETON, "");
bool first = true;
for (const auto& p : attrs->hcls->getStrAttrOffsets()) {
if (!first)
......@@ -1407,7 +1409,7 @@ public:
BoxedList* rtn = new BoxedList();
HCAttrs* attrs = self->b->getHCAttrsPtr();
RELEASE_ASSERT(attrs->hcls->type == HiddenClass::NORMAL, "");
RELEASE_ASSERT(attrs->hcls->type == HiddenClass::NORMAL || attrs->hcls->type == HiddenClass::SINGLETON, "");
for (const auto& p : attrs->hcls->getStrAttrOffsets()) {
listAppend(rtn, boxString(p.first()));
}
......@@ -1422,7 +1424,7 @@ public:
BoxedList* rtn = new BoxedList();
HCAttrs* attrs = self->b->getHCAttrsPtr();
RELEASE_ASSERT(attrs->hcls->type == HiddenClass::NORMAL, "");
RELEASE_ASSERT(attrs->hcls->type == HiddenClass::NORMAL || attrs->hcls->type == HiddenClass::SINGLETON, "");
for (const auto& p : attrs->hcls->getStrAttrOffsets()) {
listAppend(rtn, attrs->attr_list->attrs[p.second]);
}
......@@ -1437,7 +1439,7 @@ public:
BoxedList* rtn = new BoxedList();
HCAttrs* attrs = self->b->getHCAttrsPtr();
RELEASE_ASSERT(attrs->hcls->type == HiddenClass::NORMAL, "");
RELEASE_ASSERT(attrs->hcls->type == HiddenClass::NORMAL || attrs->hcls->type == HiddenClass::SINGLETON, "");
for (const auto& p : attrs->hcls->getStrAttrOffsets()) {
BoxedTuple* t = BoxedTuple::create({ boxString(p.first()), attrs->attr_list->attrs[p.second] });
listAppend(rtn, t);
......@@ -1452,7 +1454,7 @@ public:
BoxedDict* rtn = new BoxedDict();
HCAttrs* attrs = self->b->getHCAttrsPtr();
RELEASE_ASSERT(attrs->hcls->type == HiddenClass::NORMAL, "");
RELEASE_ASSERT(attrs->hcls->type == HiddenClass::NORMAL || attrs->hcls->type == HiddenClass::SINGLETON, "");
for (const auto& p : attrs->hcls->getStrAttrOffsets()) {
rtn->d[boxString(p.first())] = attrs->attr_list->attrs[p.second];
}
......@@ -1464,7 +1466,7 @@ public:
AttrWrapper* self = static_cast<AttrWrapper*>(_self);
HCAttrs* attrs = self->b->getHCAttrsPtr();
RELEASE_ASSERT(attrs->hcls->type == HiddenClass::NORMAL, "");
RELEASE_ASSERT(attrs->hcls->type == HiddenClass::NORMAL || attrs->hcls->type == HiddenClass::SINGLETON, "");
return boxInt(attrs->hcls->getStrAttrOffsets().size());
}
......@@ -1477,7 +1479,7 @@ public:
AttrWrapper* container = static_cast<AttrWrapper*>(_container);
HCAttrs* attrs = container->b->getHCAttrsPtr();
RELEASE_ASSERT(attrs->hcls->type == HiddenClass::NORMAL, "");
RELEASE_ASSERT(attrs->hcls->type == HiddenClass::NORMAL || attrs->hcls->type == HiddenClass::SINGLETON, "");
for (const auto& p : attrs->hcls->getStrAttrOffsets()) {
self->b->setattr(p.first(), attrs->attr_list->attrs[p.second], NULL);
}
......@@ -1506,14 +1508,14 @@ public:
AttrWrapperIter::AttrWrapperIter(AttrWrapper* aw) {
hcls = aw->b->getHCAttrsPtr()->hcls;
assert(hcls);
RELEASE_ASSERT(hcls->type == HiddenClass::NORMAL, "");
RELEASE_ASSERT(hcls->type == HiddenClass::NORMAL || hcls->type == HiddenClass::SINGLETON, "");
it = hcls->getStrAttrOffsets().begin();
}
Box* AttrWrapperIter::hasnext(Box* _self) {
RELEASE_ASSERT(_self->cls == attrwrapperiter_cls, "");
AttrWrapperIter* self = static_cast<AttrWrapperIter*>(_self);
RELEASE_ASSERT(self->hcls->type == HiddenClass::NORMAL, "");
RELEASE_ASSERT(self->hcls->type == HiddenClass::NORMAL || self->hcls->type == HiddenClass::SINGLETON, "");
return boxBool(self->it != self->hcls->getStrAttrOffsets().end());
}
......@@ -1521,7 +1523,7 @@ Box* AttrWrapperIter::hasnext(Box* _self) {
Box* AttrWrapperIter::next(Box* _self) {
RELEASE_ASSERT(_self->cls == attrwrapperiter_cls, "");
AttrWrapperIter* self = static_cast<AttrWrapperIter*>(_self);
RELEASE_ASSERT(self->hcls->type == HiddenClass::NORMAL, "");
RELEASE_ASSERT(self->hcls->type == HiddenClass::NORMAL || self->hcls->type == HiddenClass::SINGLETON, "");
assert(self->it != self->hcls->getStrAttrOffsets().end());
Box* r = boxString(self->it->first());
......@@ -1541,8 +1543,17 @@ Box* Box::getAttrWrapper() {
int offset = hcls->getAttrwrapperOffset();
if (offset == -1) {
Box* aw = new AttrWrapper(this);
addNewHCAttr(hcls->getAttrwrapperChild(), aw, NULL);
return aw;
if (hcls->type == HiddenClass::NORMAL) {
auto new_hcls = hcls->getAttrwrapperChild();
appendNewHCAttr(aw, NULL);
attrs->hcls = new_hcls;
return aw;
} else {
assert(hcls->type == HiddenClass::SINGLETON);
appendNewHCAttr(aw, NULL);
hcls->appendAttrwrapper();
return aw;
}
}
return attrs->attr_list->attrs[offset];
}
......
......@@ -278,6 +278,7 @@ public:
enum HCType {
NORMAL, // attributes stored in attributes array, name->offset map stored in hidden class
DICT_BACKED, // first attribute in array is a dict-like object which stores the attributes
SINGLETON, // name->offset map stored in hidden class, but hcls is mutable
} const type;
static HiddenClass* dict_backed;
......@@ -291,14 +292,21 @@ private:
}
}
// These fields only make sense for NORMAL hidden classes:
// These fields only make sense for NORMAL or SINGLETON hidden classes:
llvm::StringMap<int> attr_offsets;
ContiguousMap<llvm::StringRef, HiddenClass*, llvm::StringMap<int>> children;
// If >= 0, is the offset where we stored an attrwrapper object
int attrwrapper_offset = -1;
// These are only for NORMAL hidden classes:
ContiguousMap<llvm::StringRef, HiddenClass*, llvm::StringMap<int>> children;
HiddenClass* attrwrapper_child = NULL;
// Only for SINGLETON hidden classes:
ICInvalidator dependent_getattrs;
public:
static HiddenClass* makeSingleton() { return new HiddenClass(SINGLETON); }
static HiddenClass* makeRoot() {
#ifndef NDEBUG
static bool made = false;
......@@ -329,7 +337,7 @@ public:
if (type == DICT_BACKED)
return 1;
ASSERT(type == NORMAL, "%d", type);
ASSERT(type == NORMAL || type == SINGLETON, "%d", type);
int r = attr_offsets.size();
if (attrwrapper_offset != -1)
r += 1;
......@@ -338,18 +346,18 @@ public:
// The mapping from string attribute names to attribute offsets. There may be other objects in the attributes
// array.
// Only valid for NORMAL hidden classes
// Only valid for NORMAL or SINGLETON hidden classes
const llvm::StringMap<int>& getStrAttrOffsets() {
assert(type == NORMAL);
assert(type == NORMAL || type == SINGLETON);
return attr_offsets;
}
// Only valid for NORMAL hidden classes:
HiddenClass* getOrMakeChild(const std::string& attr);
// Only valid for NORMAL hidden classes:
// Only valid for NORMAL or SINGLETON hidden classes:
int getOffset(const std::string& attr) {
assert(type == NORMAL);
assert(type == NORMAL || type == SINGLETON);
auto it = attr_offsets.find(attr);
if (it == attr_offsets.end())
return -1;
......@@ -357,10 +365,17 @@ public:
}
int getAttrwrapperOffset() {
assert(type == NORMAL);
assert(type == NORMAL || type == SINGLETON);
return attrwrapper_offset;
}
// Only valid for SINGLETON hidden classes:
void appendAttribute(llvm::StringRef attr);
void appendAttrwrapper();
void delAttribute(llvm::StringRef attr);
void addDependence(Rewriter* rewriter);
// Only valid for NORMAL hidden classes:
HiddenClass* getAttrwrapperChild();
// Only valid for NORMAL hidden classes:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment