Commit 6160ed80 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Add a getattr rewrite "return convention"

Previously, we would inspect the combination of (return_value, exception_state)
to try to determine what the behavior of future rewrites will be.  For example,
if no attribute was returned and no exception was thrown, then we would assume
that all future times through the rewrite the behavior would be the same.  This
has caused a few bugs, and I'm about to add other cases where it won't be possible
to examine the current function's return value to determine what the future
rewrites will do.

So instead, have getattr-like functions return a "rewriter return convention" flag
that says how future rewrites will behave.
parent d9084137
...@@ -1044,8 +1044,10 @@ Box* slotTpGetattrHookInternal(Box* self, BoxedString* name, GetattrRewriteArgs* ...@@ -1044,8 +1044,10 @@ Box* slotTpGetattrHookInternal(Box* self, BoxedString* name, GetattrRewriteArgs*
if (!grewrite_args.out_success) if (!grewrite_args.out_success)
rewrite_args = NULL; rewrite_args = NULL;
else if (res) else if (res) {
rewrite_args->out_rtn = grewrite_args.out_rtn; rewrite_args->out_rtn = grewrite_args.out_rtn;
rewrite_args->out_return_convention = grewrite_args.out_return_convention;
}
} else { } else {
try { try {
res = getattrInternalGeneric(self, name, NULL, false, false, NULL, NULL); res = getattrInternalGeneric(self, name, NULL, false, false, NULL, NULL);
...@@ -1110,8 +1112,10 @@ Box* slotTpGetattrHookInternal(Box* self, BoxedString* name, GetattrRewriteArgs* ...@@ -1110,8 +1112,10 @@ Box* slotTpGetattrHookInternal(Box* self, BoxedString* name, GetattrRewriteArgs*
if (!crewrite_args.out_success) if (!crewrite_args.out_success)
rewrite_args = NULL; rewrite_args = NULL;
else else {
rewrite_args->out_rtn = crewrite_args.out_rtn; rewrite_args->out_rtn = crewrite_args.out_rtn;
rewrite_args->out_return_convention = GetattrRewriteArgs::VALID_RETURN;
}
} else { } else {
// TODO: we already fetched the getattr attribute, it would be faster to call it rather than do // TODO: we already fetched the getattr attribute, it would be faster to call it rather than do
// a second callattr. My guess though is that the gains would be small, so I would prefer to keep // a second callattr. My guess though is that the gains would be small, so I would prefer to keep
......
...@@ -33,6 +33,11 @@ Box* recordType(TypeRecorder* self, Box* obj) { ...@@ -33,6 +33,11 @@ Box* recordType(TypeRecorder* self, Box* obj) {
// The baseline JIT directly generates machine code for this function inside JitFragmentWriter::_emitRecordType. // The baseline JIT directly generates machine code for this function inside JitFragmentWriter::_emitRecordType.
// When changing this function one has to also change the bjit code. // When changing this function one has to also change the bjit code.
if (!obj) {
assert(PyErr_Occurred());
return obj;
}
BoxedClass* cls = obj->cls; BoxedClass* cls = obj->cls;
if (cls != self->last_seen) { if (cls != self->last_seen) {
self->last_seen = cls; self->last_seen = cls;
......
This diff is collapsed.
...@@ -30,6 +30,28 @@ struct GetattrRewriteArgs { ...@@ -30,6 +30,28 @@ struct GetattrRewriteArgs {
bool obj_hcls_guarded; bool obj_hcls_guarded;
bool obj_shape_guarded; // "shape" as in whether there are hcls attrs and where they live bool obj_shape_guarded; // "shape" as in whether there are hcls attrs and where they live
// We have have a couple different conventions for returning values from getattr-like functions.
// For normal code we have just two conventions:
// - the "normal" convention is that signalling the lack of an attribute is handled by throwing
// an exception (via either CAPI or C++ means). This is the only convention that CPython has.
// - our fast "no exception" convention which will return NULL and not throw an exception, not
// even a CAPI exception.
//
// For the rewriter, there are three cases:
// - we will obey the "normal" convention (VALID_RETURN)
// - we will never have anything to return and there will be no exception (NO_RETURN)
// - we don't know which of the above two will happen (NOEXC_POSSIBLE)
//
// UNSPECIFIED is used as an invalid-default to make sure that we don't implicitly assume one
// of the cases when the callee didn't explicitly signal one.
//
enum ReturnConvention {
UNSPECIFIED,
NO_RETURN,
NOEXC_POSSIBLE,
VALID_RETURN,
} out_return_convention;
GetattrRewriteArgs(Rewriter* rewriter, RewriterVar* obj, Location destination) GetattrRewriteArgs(Rewriter* rewriter, RewriterVar* obj, Location destination)
: rewriter(rewriter), : rewriter(rewriter),
obj(obj), obj(obj),
...@@ -37,7 +59,8 @@ struct GetattrRewriteArgs { ...@@ -37,7 +59,8 @@ struct GetattrRewriteArgs {
out_success(false), out_success(false),
out_rtn(NULL), out_rtn(NULL),
obj_hcls_guarded(false), obj_hcls_guarded(false),
obj_shape_guarded(false) {} obj_shape_guarded(false),
out_return_convention(UNSPECIFIED) {}
}; };
struct SetattrRewriteArgs { struct SetattrRewriteArgs {
......
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