Commit d3b6df9b authored by Kevin Modzelewski's avatar Kevin Modzelewski

Merge pull request #1153 from kmod/kill_some

Add some liveness information to the cfg
parents a0f0cb72 27b94e48
...@@ -136,8 +136,13 @@ public: ...@@ -136,8 +136,13 @@ public:
bool visit_name(AST_Name* node) { bool visit_name(AST_Name* node) {
if (node->ctx_type == AST_TYPE::Load) if (node->ctx_type == AST_TYPE::Load)
_doLoad(node->id, node); _doLoad(node->id, node);
else if (node->ctx_type == AST_TYPE::Store || node->ctx_type == AST_TYPE::Del else if (node->ctx_type == AST_TYPE::Del) {
|| node->ctx_type == AST_TYPE::Param) // Hack: we don't have a bytecode for temporary-kills:
if (node->id.s()[0] == '#')
return true;
_doLoad(node->id, node);
_doStore(node->id);
} else if (node->ctx_type == AST_TYPE::Store || node->ctx_type == AST_TYPE::Param)
_doStore(node->id); _doStore(node->id);
else { else {
ASSERT(0, "%d", node->ctx_type); ASSERT(0, "%d", node->ctx_type);
...@@ -145,6 +150,7 @@ public: ...@@ -145,6 +150,7 @@ public:
} }
return true; return true;
} }
bool visit_alias(AST_alias* node) { bool visit_alias(AST_alias* node) {
InternedString name = node->name; InternedString name = node->name;
if (node->asname.s().size()) if (node->asname.s().size())
......
...@@ -693,6 +693,19 @@ void RewriterVar::setAttr(int offset, RewriterVar* val, SetattrType type) { ...@@ -693,6 +693,19 @@ void RewriterVar::setAttr(int offset, RewriterVar* val, SetattrType type) {
rewriter->addAction([=]() { rewriter->_setAttr(this, offset, val); }, { this, val }, ActionType::MUTATION); rewriter->addAction([=]() { rewriter->_setAttr(this, offset, val); }, { this, val }, ActionType::MUTATION);
} }
void RewriterVar::replaceAttr(int offset, RewriterVar* val, bool prev_nullable) {
RewriterVar* prev = this->getAttr(offset);
this->setAttr(offset, val, SetattrType::HANDED_OFF);
val->refConsumed();
if (prev_nullable) {
prev->setNullable(true);
prev->xdecref();
} else
prev->decref();
}
void Rewriter::_setAttr(RewriterVar* ptr, int offset, RewriterVar* val) { void Rewriter::_setAttr(RewriterVar* ptr, int offset, RewriterVar* val) {
if (LOG_IC_ASSEMBLY) if (LOG_IC_ASSEMBLY)
assembler->comment("_setAttr"); assembler->comment("_setAttr");
......
...@@ -155,6 +155,15 @@ public: ...@@ -155,6 +155,15 @@ public:
REFUSED, REFUSED,
}; };
void setAttr(int offset, RewriterVar* other, SetattrType type = SetattrType::UNKNOWN); void setAttr(int offset, RewriterVar* other, SetattrType type = SetattrType::UNKNOWN);
// Replaces an owned ref with another one. Does the equivalent of:
// Box* prev = this[offset];
// this[offset] = new_val;
// Py_[X]DECREF(prev);
//
// Calls new_val->refConsumed() for you.
void replaceAttr(int offset, RewriterVar* new_val, bool prev_nullable);
RewriterVar* cmp(AST_TYPE::AST_TYPE cmp_type, RewriterVar* other, Location loc = Location::any()); RewriterVar* cmp(AST_TYPE::AST_TYPE cmp_type, RewriterVar* other, Location loc = Location::any());
RewriterVar* toBool(Location loc = Location::any()); RewriterVar* toBool(Location loc = Location::any());
......
...@@ -1080,6 +1080,24 @@ Value ASTInterpreter::visit_return(AST_Return* node) { ...@@ -1080,6 +1080,24 @@ Value ASTInterpreter::visit_return(AST_Return* node) {
finishJITing(); finishJITing();
} }
// Some day, we should make sure that all temporaries got deleted (and decrefed) at the right time:
#if 0
bool temporaries_alive = false;
#ifndef NDEBUG
for (auto&& v : getSymVRegMap()) {
if (v.first.s()[0] == '#' && vregs[v.second]) {
fprintf(stderr, "%s still alive\n", v.first.c_str());
temporaries_alive = true;
}
}
if (temporaries_alive)
source_info->cfg->print();
assert(!temporaries_alive);
#endif
#endif
next_block = 0; next_block = 0;
return s; return s;
} }
...@@ -1332,14 +1350,21 @@ Value ASTInterpreter::visit_delete(AST_Delete* node) { ...@@ -1332,14 +1350,21 @@ Value ASTInterpreter::visit_delete(AST_Delete* node) {
jit->emitDelName(target->id); jit->emitDelName(target->id);
ASTInterpreterJitInterface::delNameHelper(this, target->id); ASTInterpreterJitInterface::delNameHelper(this, target->id);
} else { } else {
abortJITing();
assert(vst == ScopeInfo::VarScopeType::FAST); assert(vst == ScopeInfo::VarScopeType::FAST);
assert(getSymVRegMap().count(target->id)); assert(getSymVRegMap().count(target->id));
assert(getSymVRegMap()[target->id] == target->vreg); assert(getSymVRegMap()[target->id] == target->vreg);
if (vregs[target->vreg] == 0) {
assertNameDefined(0, target->id.c_str(), NameError, true /* local_var_msg */); if (target->id.s()[0] == '#') {
return Value(); assert(vregs[target->vreg] != NULL);
if (jit)
jit->emitKillTemporary(target->id, target->vreg);
} else {
abortJITing();
if (vregs[target->vreg] == 0) {
assertNameDefined(0, target->id.c_str(), NameError, true /* local_var_msg */);
return Value();
}
} }
Py_DECREF(vregs[target->vreg]); Py_DECREF(vregs[target->vreg]);
...@@ -1635,6 +1660,7 @@ Value ASTInterpreter::visit_name(AST_Name* node) { ...@@ -1635,6 +1660,7 @@ Value ASTInterpreter::visit_name(AST_Name* node) {
switch (node->lookup_type) { switch (node->lookup_type) {
case ScopeInfo::VarScopeType::GLOBAL: { case ScopeInfo::VarScopeType::GLOBAL: {
assert(!node->is_kill);
Value v; Value v;
if (jit) if (jit)
v.var = jit->emitGetGlobal(frame_info.globals, node->id.getBox()); v.var = jit->emitGetGlobal(frame_info.globals, node->id.getBox());
...@@ -1643,6 +1669,7 @@ Value ASTInterpreter::visit_name(AST_Name* node) { ...@@ -1643,6 +1669,7 @@ Value ASTInterpreter::visit_name(AST_Name* node) {
return v; return v;
} }
case ScopeInfo::VarScopeType::DEREF: { case ScopeInfo::VarScopeType::DEREF: {
assert(!node->is_kill);
return Value(ASTInterpreterJitInterface::derefHelper(this, node->id), return Value(ASTInterpreterJitInterface::derefHelper(this, node->id),
jit ? jit->emitDeref(node->id) : NULL); jit ? jit->emitDeref(node->id) : NULL);
} }
...@@ -1651,22 +1678,35 @@ Value ASTInterpreter::visit_name(AST_Name* node) { ...@@ -1651,22 +1678,35 @@ Value ASTInterpreter::visit_name(AST_Name* node) {
Value v; Value v;
if (jit) { if (jit) {
bool is_live = true; bool is_live = true;
if (node->lookup_type == ScopeInfo::VarScopeType::FAST) if (node->is_kill) {
is_live = false;
assert(!source_info->getLiveness()->isLiveAtEnd(node->id, current_block));
} else if (node->lookup_type == ScopeInfo::VarScopeType::FAST)
is_live = source_info->getLiveness()->isLiveAtEnd(node->id, current_block); is_live = source_info->getLiveness()->isLiveAtEnd(node->id, current_block);
if (is_live) if (is_live) {
assert(!node->is_kill);
v.var = jit->emitGetLocal(node->id, node->vreg); v.var = jit->emitGetLocal(node->id, node->vreg);
else } else {
v.var = jit->emitGetBlockLocal(node->id, node->vreg); v.var = jit->emitGetBlockLocal(node->id, node->vreg);
if (node->is_kill) {
assert(node->id.s()[0] == '#');
jit->emitKillTemporary(node->id, node->vreg);
}
}
} }
assert(node->vreg >= 0); assert(node->vreg >= 0);
assert(getSymVRegMap().count(node->id)); assert(getSymVRegMap().count(node->id));
assert(getSymVRegMap()[node->id] == node->vreg); assert(getSymVRegMap()[node->id] == node->vreg);
Box* val = vregs[node->vreg]; Box* val = vregs[node->vreg];
if (val) { if (val) {
Py_INCREF(val);
v.o = val; v.o = val;
if (node->is_kill)
vregs[node->vreg] = NULL;
else
Py_INCREF(val);
return v; return v;
} }
...@@ -1674,6 +1714,7 @@ Value ASTInterpreter::visit_name(AST_Name* node) { ...@@ -1674,6 +1714,7 @@ Value ASTInterpreter::visit_name(AST_Name* node) {
RELEASE_ASSERT(0, "should be unreachable"); RELEASE_ASSERT(0, "should be unreachable");
} }
case ScopeInfo::VarScopeType::NAME: { case ScopeInfo::VarScopeType::NAME: {
assert(!node->is_kill && "we might need to support this");
Value v; Value v;
if (jit) if (jit)
v.var = jit->emitGetBoxedLocal(node->id.getBox()); v.var = jit->emitGetBoxedLocal(node->id.getBox());
......
...@@ -379,6 +379,10 @@ RewriterVar* JitFragmentWriter::emitGetBlockLocal(InternedString s, int vreg) { ...@@ -379,6 +379,10 @@ RewriterVar* JitFragmentWriter::emitGetBlockLocal(InternedString s, int vreg) {
return it->second; return it->second;
} }
void JitFragmentWriter::emitKillTemporary(InternedString s, int vreg) {
emitSetLocal(s, vreg, false, imm(nullptr));
}
RewriterVar* JitFragmentWriter::emitGetBoxedLocal(BoxedString* s) { RewriterVar* JitFragmentWriter::emitGetBoxedLocal(BoxedString* s) {
RewriterVar* boxed_locals = emitGetBoxedLocals(); RewriterVar* boxed_locals = emitGetBoxedLocals();
RewriterVar* globals = getInterp()->getAttr(ASTInterpreterJitInterface::getGlobalsOffset()); RewriterVar* globals = getInterp()->getAttr(ASTInterpreterJitInterface::getGlobalsOffset());
...@@ -674,16 +678,13 @@ void JitFragmentWriter::emitSetLocal(InternedString s, int vreg, bool set_closur ...@@ -674,16 +678,13 @@ void JitFragmentWriter::emitSetLocal(InternedString s, int vreg, bool set_closur
v); v);
v->refConsumed(); v->refConsumed();
} else { } else {
RewriterVar* prev = vregs_array->getAttr(8 * vreg)->setNullable(true); // TODO With definedness analysis, we could know whether we needed to emit an decref/xdecref/neither.
vregs_array->setAttr(8 * vreg, v, RewriterVar::SetattrType::HANDED_OFF);
v->refConsumed();
// TODO With definedness analysis, we could know whether we can skip this check (definitely defined)
// or not even load the previous value (definitely undefined).
// The issue is that definedness analysis is somewhat expensive to compute, so we don't compute it // The issue is that definedness analysis is somewhat expensive to compute, so we don't compute it
// for the bjit. We could try calculating it (which would require some re-plumbing), which might help // for the bjit. We could try calculating it (which would require some re-plumbing), which might help
// but I suspect is not that big a deal as long as the llvm jit implements this kind of optimization. // but I suspect is not that big a deal as long as the llvm jit implements this kind of optimization.
prev->xdecref(); bool prev_nullable = true;
vregs_array->replaceAttr(8 * vreg, v, prev_nullable);
} }
if (LOG_BJIT_ASSEMBLY) if (LOG_BJIT_ASSEMBLY)
comment("BJIT: emitSetLocal() end"); comment("BJIT: emitSetLocal() end");
......
...@@ -230,6 +230,7 @@ public: ...@@ -230,6 +230,7 @@ public:
RewriterVar* emitExceptionMatches(RewriterVar* v, RewriterVar* cls); RewriterVar* emitExceptionMatches(RewriterVar* v, RewriterVar* cls);
RewriterVar* emitGetAttr(RewriterVar* obj, BoxedString* s, AST_expr* node); RewriterVar* emitGetAttr(RewriterVar* obj, BoxedString* s, AST_expr* node);
RewriterVar* emitGetBlockLocal(InternedString s, int vreg); RewriterVar* emitGetBlockLocal(InternedString s, int vreg);
void emitKillTemporary(InternedString s, int vreg);
RewriterVar* emitGetBoxedLocal(BoxedString* s); RewriterVar* emitGetBoxedLocal(BoxedString* s);
RewriterVar* emitGetBoxedLocals(); RewriterVar* emitGetBoxedLocals();
RewriterVar* emitGetClsAttr(RewriterVar* obj, BoxedString* s); RewriterVar* emitGetClsAttr(RewriterVar* obj, BoxedString* s);
......
...@@ -2123,6 +2123,12 @@ private: ...@@ -2123,6 +2123,12 @@ private:
} }
void _doDelName(AST_Name* target, const UnwindInfo& unw_info) { void _doDelName(AST_Name* target, const UnwindInfo& unw_info) {
// Hack: we don't have a bytecode for temporary-kills:
if (target->id.s()[0] == '#') {
// The refcounter will automatically delete this object.
return;
}
auto scope_info = irstate->getScopeInfo(); auto scope_info = irstate->getScopeInfo();
ScopeInfo::VarScopeType vst = scope_info->getScopeTypeOfName(target->id); ScopeInfo::VarScopeType vst = scope_info->getScopeTypeOfName(target->id);
if (vst == ScopeInfo::VarScopeType::GLOBAL) { if (vst == ScopeInfo::VarScopeType::GLOBAL) {
......
...@@ -1630,7 +1630,8 @@ bool PrintVisitor::visit_suite(AST_Suite* node) { ...@@ -1630,7 +1630,8 @@ bool PrintVisitor::visit_suite(AST_Suite* node) {
bool PrintVisitor::visit_name(AST_Name* node) { bool PrintVisitor::visit_name(AST_Name* node) {
stream << node->id.s(); stream << node->id.s();
// printf("%s(%d)", node->id.c_str(), node->ctx_type); // Uncomment this line to see which names are kills:
// if (node->is_kill) stream << "<k>";
return false; return false;
} }
......
...@@ -735,6 +735,8 @@ public: ...@@ -735,6 +735,8 @@ public:
// the zero based index of this variable inside the vregs array. If uninitialized it's value is -1. // the zero based index of this variable inside the vregs array. If uninitialized it's value is -1.
int vreg; int vreg;
bool is_kill = false;
virtual void accept(ASTVisitor* v); virtual void accept(ASTVisitor* v);
virtual void* accept_expr(ExprVisitor* v); virtual void* accept_expr(ExprVisitor* v);
......
This diff is collapsed.
...@@ -622,12 +622,12 @@ struct GetattrRewriteArgs; ...@@ -622,12 +622,12 @@ struct GetattrRewriteArgs;
struct DelattrRewriteArgs; struct DelattrRewriteArgs;
// Helper function around PyString_InternFromString: // Helper function around PyString_InternFromString:
BoxedString* internStringImmortal(llvm::StringRef s); BoxedString* internStringImmortal(llvm::StringRef s) noexcept;
// Callers should use this function if they can accept mortal string objects. // Callers should use this function if they can accept mortal string objects.
// FIXME For now it just returns immortal strings, but at least we can use it // FIXME For now it just returns immortal strings, but at least we can use it
// to start documenting the places that can take mortal strings. // to start documenting the places that can take mortal strings.
inline BoxedString* internStringMortal(llvm::StringRef s) { inline BoxedString* internStringMortal(llvm::StringRef s) noexcept {
return internStringImmortal(s); return internStringImmortal(s);
} }
......
...@@ -1512,13 +1512,8 @@ void Box::setattr(BoxedString* attr, BORROWED(Box*) val, SetattrRewriteArgs* rew ...@@ -1512,13 +1512,8 @@ void Box::setattr(BoxedString* attr, BORROWED(Box*) val, SetattrRewriteArgs* rew
RewriterVar* r_hattrs RewriterVar* r_hattrs
= rewrite_args->obj->getAttr(cls->attrs_offset + offsetof(HCAttrs, attr_list), Location::any()); = rewrite_args->obj->getAttr(cls->attrs_offset + offsetof(HCAttrs, attr_list), Location::any());
// Don't need to do anything: just getting it and setting it to OWNED r_hattrs->replaceAttr(offset * sizeof(Box*) + offsetof(HCAttrs::AttrList, attrs),
// will tell the auto-refcount system to decref it. rewrite_args->attrval, /* prev_nullable */ false);
r_hattrs->getAttr(offset * sizeof(Box*) + offsetof(HCAttrs::AttrList, attrs))
->setType(RefType::OWNED);
r_hattrs->setAttr(offset * sizeof(Box*) + offsetof(HCAttrs::AttrList, attrs), rewrite_args->attrval,
RewriterVar::SetattrType::HANDED_OFF);
rewrite_args->attrval->refConsumed();
rewrite_args->out_success = true; rewrite_args->out_success = true;
} }
......
...@@ -371,7 +371,7 @@ extern "C" PyObject* PyString_InternFromString(const char* s) noexcept { ...@@ -371,7 +371,7 @@ extern "C" PyObject* PyString_InternFromString(const char* s) noexcept {
return internStringImmortal(s); return internStringImmortal(s);
} }
BoxedString* internStringImmortal(llvm::StringRef s) { BoxedString* internStringImmortal(llvm::StringRef s) noexcept {
auto& entry = interned_strings[s]; auto& entry = interned_strings[s];
if (!entry) { if (!entry) {
num_interned_strings.log(); num_interned_strings.log();
......
from _weakref import ref
# Test to make sure that we clear local variables at the right time:
def f1():
def f():
pass
r = ref(f)
print type(r())
# del f
f = 1
assert not r()
for i in xrange(40):
f1()
def f3():
class MyIter(object):
def __init__(self):
self.n = 5
def __del__(self):
print "deleting iter"
def next(self):
if self.n:
self.n -= 1
return self.n
raise StopIteration()
class MyIterable(object):
def __iter__(self):
return MyIter()
for i in MyIterable():
print i
else:
print -1
f3()
# expected: fail
# - finalization (let alone resurrection) not implemented yet
# Objects are allowed to resurrect themselves in their __del__ methods... # Objects are allowed to resurrect themselves in their __del__ methods...
# Note: the behavior here will differ from cPython and maybe PyPy
x = None x = None
running = True running = True
......
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