Commit e3dc92d1 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Add setattrInternal

This should fix the bad behavior we had where setGlobal would only get rewritten
by having its call to setattr() get inlined, and having setattr() then think it
was being called directly.  This was both brittle (only worked on release builds)
and incorrect (there were checks that happened before calling into setattr, which
needed to be emitted as guards).

I had hacked in an attribute((always_inline)) to at least deal with the brittleness,
but this should be a fix for real.  And let us reenable the gcc builds.
parent 2f40d89e
...@@ -3105,37 +3105,14 @@ void setattrGeneric(Box* obj, BoxedString* attr, STOLEN(Box*) val, SetattrRewrit ...@@ -3105,37 +3105,14 @@ void setattrGeneric(Box* obj, BoxedString* attr, STOLEN(Box*) val, SetattrRewrit
template void setattrGeneric<NOT_REWRITABLE>(Box* obj, BoxedString* attr, Box* val, SetattrRewriteArgs* rewrite_args); template void setattrGeneric<NOT_REWRITABLE>(Box* obj, BoxedString* attr, Box* val, SetattrRewriteArgs* rewrite_args);
template void setattrGeneric<REWRITABLE>(Box* obj, BoxedString* attr, Box* val, SetattrRewriteArgs* rewrite_args); template void setattrGeneric<REWRITABLE>(Box* obj, BoxedString* attr, Box* val, SetattrRewriteArgs* rewrite_args);
extern "C" void setattr(Box* obj, BoxedString* attr, STOLEN(Box*) attr_val) { void setattrInternal(Box* obj, BoxedString* attr, STOLEN(Box*) attr_val, SetattrRewriteArgs* rewrite_args) {
STAT_TIMER(t0, "us_timer_slowpath_setattr", 10);
static StatCounter slowpath_setattr("slowpath_setattr");
slowpath_setattr.log();
if (obj->cls->tp_setattr) {
STAT_TIMER(t1, "us_timer_slowpath_tpsetattr", 10);
assert(attr->data()[attr->size()] == '\0');
AUTO_DECREF(attr_val);
int rtn = obj->cls->tp_setattr(obj, const_cast<char*>(attr->data()), attr_val);
if (rtn)
throwCAPIException();
return;
}
std::unique_ptr<Rewriter> rewriter(
Rewriter::createRewriter(__builtin_extract_return_addr(__builtin_return_address(0)), 3, "setattr"));
setattrofunc tp_setattro = obj->cls->tp_setattro; setattrofunc tp_setattro = obj->cls->tp_setattro;
assert(tp_setattro); assert(tp_setattro);
assert(!obj->cls->tp_setattr); assert(!obj->cls->tp_setattr);
if (rewriter.get()) { if (rewrite_args) {
rewriter->getArg(0)->setType(RefType::BORROWED); auto r_cls = rewrite_args->obj->getAttr(offsetof(Box, cls));
rewriter->getArg(1)->setType(RefType::BORROWED);
rewriter->getArg(2)->setType(RefType::OWNED);
auto r_cls = rewriter->getArg(0)->getAttr(offsetof(Box, cls));
// rewriter->trap(); // rewriter->trap();
r_cls->addAttrGuard(offsetof(BoxedClass, tp_setattr), 0); r_cls->addAttrGuard(offsetof(BoxedClass, tp_setattr), 0);
r_cls->addAttrGuard(offsetof(BoxedClass, tp_setattro), (intptr_t)tp_setattro); r_cls->addAttrGuard(offsetof(BoxedClass, tp_setattro), (intptr_t)tp_setattro);
...@@ -3147,19 +3124,14 @@ extern "C" void setattr(Box* obj, BoxedString* attr, STOLEN(Box*) attr_val) { ...@@ -3147,19 +3124,14 @@ extern "C" void setattr(Box* obj, BoxedString* attr, STOLEN(Box*) attr_val) {
RewriterVar* r_setattr; RewriterVar* r_setattr;
if (tp_setattro == instance_setattro) { if (tp_setattro == instance_setattro) {
if (rewriter.get()) { instanceSetattroInternal(obj, attr, attr_val, rewrite_args);
SetattrRewriteArgs rewrite_args(rewriter.get(), rewriter->getArg(0), rewriter->getArg(2));
instanceSetattroInternal(obj, attr, attr_val, &rewrite_args);
if (rewrite_args.out_success)
rewriter->commit();
} else
instanceSetattroInternal(obj, attr, attr_val, NULL);
return; return;
} else if (tp_setattro != PyObject_GenericSetAttr) { } else if (tp_setattro != PyObject_GenericSetAttr) {
// TODO: rewrite these cases?
#if 0
static BoxedString* setattr_str = getStaticString("__setattr__"); static BoxedString* setattr_str = getStaticString("__setattr__");
if (rewriter.get()) { if (rewrite_args) {
GetattrRewriteArgs rewrite_args(rewriter.get(), rewriter->getArg(0)->getAttr(offsetof(Box, cls)), GetattrRewriteArgs rewrite_args(rewrite_args->rewriter, rewrite_args->obj->getAttr(offsetof(Box, cls)),
Location::any()); Location::any());
setattr = typeLookup(obj->cls, setattr_str, &rewrite_args); setattr = typeLookup(obj->cls, setattr_str, &rewrite_args);
assert(setattr); assert(setattr);
...@@ -3174,37 +3146,28 @@ extern "C" void setattr(Box* obj, BoxedString* attr, STOLEN(Box*) attr_val) { ...@@ -3174,37 +3146,28 @@ extern "C" void setattr(Box* obj, BoxedString* attr, STOLEN(Box*) attr_val) {
} else { } else {
// setattr = typeLookup(obj->cls, setattr_str); // setattr = typeLookup(obj->cls, setattr_str);
} }
#endif
} }
// This is a borrowed reference so we don't need to register it
static Box* object_setattr = object_cls->getattr(getStaticString("__setattr__"));
// I guess this check makes it ok for us to just rely on having guarded on the value of setattr without
// invalidating on deallocation, since we assume that object.__setattr__ will never get deallocated.
if (tp_setattro == PyObject_GenericSetAttr) { if (tp_setattro == PyObject_GenericSetAttr) {
if (rewriter.get()) { setattrGeneric<REWRITABLE>(obj, attr, attr_val, rewrite_args);
// rewriter->trap();
SetattrRewriteArgs rewrite_args(rewriter.get(), rewriter->getArg(0), rewriter->getArg(2));
setattrGeneric<REWRITABLE>(obj, attr, attr_val, &rewrite_args);
if (rewrite_args.out_success)
rewriter->commit();
} else {
setattrGeneric<NOT_REWRITABLE>(obj, attr, attr_val, NULL);
}
return; return;
} }
AUTO_DECREF(attr_val); AUTO_DECREF(attr_val);
if (rewriter.get()) { // TODO actually rewrite this?
#if 0
if (rewrite_args) {
assert(setattr); assert(setattr);
// TODO actually rewrite this?
setattr = processDescriptor(setattr, obj, obj->cls); setattr = processDescriptor(setattr, obj, obj->cls);
AUTO_DECREF(setattr); AUTO_DECREF(setattr);
autoDecref( autoDecref(
runtimeCallInternal<CXX, REWRITABLE>(setattr, NULL, ArgPassSpec(2), attr, attr_val, NULL, NULL, NULL)); runtimeCallInternal<CXX, REWRITABLE>(setattr, NULL, ArgPassSpec(2), attr, attr_val, NULL, NULL, NULL));
} else { } else
#endif
{
STAT_TIMER(t0, "us_timer_slowpath_tpsetattro", 10); STAT_TIMER(t0, "us_timer_slowpath_tpsetattro", 10);
int r = tp_setattro(obj, attr, attr_val); int r = tp_setattro(obj, attr, attr_val);
if (r) if (r)
...@@ -3212,6 +3175,39 @@ extern "C" void setattr(Box* obj, BoxedString* attr, STOLEN(Box*) attr_val) { ...@@ -3212,6 +3175,39 @@ extern "C" void setattr(Box* obj, BoxedString* attr, STOLEN(Box*) attr_val) {
} }
} }
extern "C" void setattr(Box* obj, BoxedString* attr, STOLEN(Box*) attr_val) {
STAT_TIMER(t0, "us_timer_slowpath_setattr", 10);
static StatCounter slowpath_setattr("slowpath_setattr");
slowpath_setattr.log();
if (obj->cls->tp_setattr) {
STAT_TIMER(t1, "us_timer_slowpath_tpsetattr", 10);
assert(attr->data()[attr->size()] == '\0');
AUTO_DECREF(attr_val);
int rtn = obj->cls->tp_setattr(obj, const_cast<char*>(attr->data()), attr_val);
if (rtn)
throwCAPIException();
return;
}
std::unique_ptr<Rewriter> rewriter(
Rewriter::createRewriter(__builtin_extract_return_addr(__builtin_return_address(0)), 3, "setattr"));
if (rewriter.get()) {
SetattrRewriteArgs rewrite_args(rewriter.get(), rewriter->getArg(0)->setType(RefType::BORROWED),
rewriter->getArg(2)->setType(RefType::OWNED));
rewriter->getArg(1)->setType(RefType::BORROWED);
setattrInternal(obj, attr, attr_val, &rewrite_args);
if (rewrite_args.out_success)
rewriter->commit();
} else {
setattrInternal(obj, attr, attr_val, NULL);
}
}
static bool nonzeroHelper(STOLEN(Box*) r) { static bool nonzeroHelper(STOLEN(Box*) r) {
AUTO_DECREF(r); AUTO_DECREF(r);
...@@ -7284,14 +7280,23 @@ extern "C" void setGlobal(Box* globals, BoxedString* name, STOLEN(Box*) value) { ...@@ -7284,14 +7280,23 @@ extern "C" void setGlobal(Box* globals, BoxedString* name, STOLEN(Box*) value) {
} }
if (globals->cls == module_cls) { if (globals->cls == module_cls) {
// Note: in optimized builds, this will be a tail call, which will std::unique_ptr<Rewriter> rewriter(
// preserve the return address, letting the setattr() call rewrite itself. Rewriter::createRewriter(__builtin_extract_return_addr(__builtin_return_address(0)), 3, "setattr"));
// XXX this isn't really safe in general, since the guards that led to this
// path need to end up in the rewrite. I think this is safe for now since if (rewriter.get()) {
// writing the module case won't accidentally work for the dict case, but SetattrRewriteArgs rewrite_args(rewriter.get(), rewriter->getArg(0)->setType(RefType::BORROWED),
// we should make all the entrypoints (the ones that look at the return address) rewriter->getArg(2)->setType(RefType::OWNED));
// be noinline. rewriter->getArg(1)->setType(RefType::BORROWED);
setattr(static_cast<BoxedModule*>(globals), name, value);
rewrite_args.obj->addAttrGuard(offsetof(Box, cls), (uint64_t)globals->cls);
setattrInternal(globals, name, value, &rewrite_args);
if (rewrite_args.out_success)
rewriter->commit();
} else {
setattrInternal(globals, name, value, NULL);
}
} else { } else {
RELEASE_ASSERT(globals->cls == dict_cls, "%s", globals->cls->tp_name); RELEASE_ASSERT(globals->cls == dict_cls, "%s", globals->cls->tp_name);
int r = PyDict_SetItem(globals, name, value); int r = PyDict_SetItem(globals, name, value);
......
...@@ -56,7 +56,7 @@ extern "C" Box* getattr_capi(Box* obj, BoxedString* attr) noexcept __attribute__ ...@@ -56,7 +56,7 @@ extern "C" Box* getattr_capi(Box* obj, BoxedString* attr) noexcept __attribute__
extern "C" Box* getattrMaybeNonstring(Box* obj, Box* attr); extern "C" Box* getattrMaybeNonstring(Box* obj, Box* attr);
// XXX: testing. this tail-calls in optimized builds so force it to inline for unoptimized as well to get the same // XXX: testing. this tail-calls in optimized builds so force it to inline for unoptimized as well to get the same
// behavior. // behavior.
extern "C" void setattr(Box* obj, BoxedString* attr, STOLEN(Box*) attr_val) __attribute__((always_inline)); extern "C" void setattr(Box* obj, BoxedString* attr, STOLEN(Box*) attr_val) __attribute__((noinline));
extern "C" void delattr(Box* obj, BoxedString* attr) __attribute__((noinline)); extern "C" void delattr(Box* obj, BoxedString* attr) __attribute__((noinline));
extern "C" void delattrGeneric(Box* obj, BoxedString* attr, DelattrRewriteArgs* rewrite_args); extern "C" void delattrGeneric(Box* obj, BoxedString* attr, DelattrRewriteArgs* rewrite_args);
extern "C" bool nonzero(Box* obj) __attribute__((noinline)); extern "C" bool nonzero(Box* obj) __attribute__((noinline));
......
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