Commit 7fb7aaa5 authored by Michael Arntzenius's avatar Michael Arntzenius

with statements now handle exceptions properly

parent be19d931
...@@ -738,6 +738,8 @@ CompilerVariable* makeFunction(IREmitter& emitter, CLFunction* f, CompilerVariab ...@@ -738,6 +738,8 @@ CompilerVariable* makeFunction(IREmitter& emitter, CLFunction* f, CompilerVariab
llvm::Value* isGenerator_v = llvm::ConstantInt::get(g.i1, isGenerator, false); llvm::Value* isGenerator_v = llvm::ConstantInt::get(g.i1, isGenerator, false);
// We know this function call can't throw, so it's safe to use emitter.getBuilder()->CreateCall() rather than
// emitter.createCall().
llvm::Value* boxed = emitter.getBuilder()->CreateCall( llvm::Value* boxed = emitter.getBuilder()->CreateCall(
g.funcs.boxCLFunction, g.funcs.boxCLFunction,
std::vector<llvm::Value*>{ embedConstantPtr(f, g.llvm_clfunction_type_ptr), closure_v, isGenerator_v, scratch, std::vector<llvm::Value*>{ embedConstantPtr(f, g.llvm_clfunction_type_ptr), closure_v, isGenerator_v, scratch,
......
...@@ -286,7 +286,7 @@ private: ...@@ -286,7 +286,7 @@ private:
protected: protected:
void drop(IREmitter& emitter) override { type->drop(emitter, this); } void drop(IREmitter& emitter) override { type->drop(emitter, this); }
void grab(IREmitter& emmitter) override { type->grab(emmitter, this); } void grab(IREmitter& emitter) override { type->grab(emitter, this); }
public: public:
ValuedCompilerVariable(T* type, V value, bool grabbed) : CompilerVariable(grabbed), type(type), value(value) { ValuedCompilerVariable(T* type, V value, bool grabbed) : CompilerVariable(grabbed), type(type), value(value) {
......
This diff is collapsed.
...@@ -291,6 +291,7 @@ private: ...@@ -291,6 +291,7 @@ private:
llvm::BasicBlock* curblock; llvm::BasicBlock* curblock;
IREmitterImpl emitter; IREmitterImpl emitter;
// symbol_table tracks which (non-global) python variables are bound to which CompilerVariables
SymbolTable symbol_table; SymbolTable symbol_table;
std::unordered_map<CFGBlock*, llvm::BasicBlock*>& entry_blocks; std::unordered_map<CFGBlock*, llvm::BasicBlock*>& entry_blocks;
CFGBlock* myblock; CFGBlock* myblock;
...@@ -883,6 +884,7 @@ private: ...@@ -883,6 +884,7 @@ private:
return closure->getattr(emitter, getEmptyOpInfo(unw_info), &node->id.str(), false); return closure->getattr(emitter, getEmptyOpInfo(unw_info), &node->id.str(), false);
} else { } else {
// vst is one of {FAST, CLOSURE, NAME}
if (symbol_table.find(node->id) == symbol_table.end()) { if (symbol_table.find(node->id) == symbol_table.end()) {
// classdefs have different scoping rules than functions: // classdefs have different scoping rules than functions:
if (vst == ScopeInfo::VarScopeType::NAME) { if (vst == ScopeInfo::VarScopeType::NAME) {
...@@ -1225,6 +1227,12 @@ private: ...@@ -1225,6 +1227,12 @@ private:
cur = val; cur = val;
} }
// whether a Python variable FOO might be undefined or not is determined by whether the corresponding is_defined_FOO
// variable is present in our symbol table. If it is, then it *might* be undefined. If it isn't, then it either is
// definitely defined, or definitely isn't.
//
// to check whether a variable is in our symbol table, call _getFake with allow_missing = true and check whether the
// result is NULL.
CompilerVariable* _getFake(InternedString name, bool allow_missing = false) { CompilerVariable* _getFake(InternedString name, bool allow_missing = false) {
assert(name.str()[0] == '!'); assert(name.str()[0] == '!');
auto it = symbol_table.find(name); auto it = symbol_table.find(name);
...@@ -1243,6 +1251,7 @@ private: ...@@ -1243,6 +1251,7 @@ private:
return rtn; return rtn;
} }
// only updates symbol_table if we're *not* setting a global
void _doSet(InternedString name, CompilerVariable* val, UnwindInfo unw_info) { void _doSet(InternedString name, CompilerVariable* val, UnwindInfo unw_info) {
assert(name.str() != "None"); assert(name.str() != "None");
...@@ -1702,7 +1711,7 @@ private: ...@@ -1702,7 +1711,7 @@ private:
// that case asking it to convert to itself ends up just being an incvref // that case asking it to convert to itself ends up just being an incvref
// and doesn't end up emitting an incref+decref pair. // and doesn't end up emitting an incref+decref pair.
// This could also be handled by casting from the CompilerVariable to // This could also be handled by casting from the CompilerVariable to
// ConcreteCOmpilerVariable, but this way feels a little more robust to me. // ConcreteCompilerVariable, but this way feels a little more robust to me.
ConcreteCompilerType* opt_rtn_type = irstate->getReturnType(); ConcreteCompilerType* opt_rtn_type = irstate->getReturnType();
if (irstate->getReturnType()->llvmType() == val->getConcreteType()->llvmType()) if (irstate->getReturnType()->llvmType() == val->getConcreteType()->llvmType())
opt_rtn_type = val->getConcreteType(); opt_rtn_type = val->getConcreteType();
...@@ -2060,9 +2069,9 @@ private: ...@@ -2060,9 +2069,9 @@ private:
SourceInfo* source = irstate->getSourceInfo(); SourceInfo* source = irstate->getSourceInfo();
ScopeInfo* scope_info = irstate->getScopeInfo(); ScopeInfo* scope_info = irstate->getScopeInfo();
// Additional names to remove; remove them after iteration is done to new mess up the iterators // Additional names to remove; remove them after iteration is done to not mess up the iterators
std::vector<InternedString> also_remove; std::vector<InternedString> also_remove;
for (SymbolTable::iterator it = symbol_table.begin(); it != symbol_table.end();) { for (auto it = symbol_table.begin(); it != symbol_table.end();) {
if (allowableFakeEndingSymbol(it->first)) { if (allowableFakeEndingSymbol(it->first)) {
++it; ++it;
continue; continue;
...@@ -2073,7 +2082,7 @@ private: ...@@ -2073,7 +2082,7 @@ private:
if (!source->liveness->isLiveAtEnd(it->first, myblock)) { if (!source->liveness->isLiveAtEnd(it->first, myblock)) {
// printf("%s dead at end of %d; grabbed = %d, %d vrefs\n", it->first.c_str(), myblock->idx, // printf("%s dead at end of %d; grabbed = %d, %d vrefs\n", it->first.c_str(), myblock->idx,
// it->second->isGrabbed(), it->second->getVrefs()); // it->second->isGrabbed(), it->second->getVrefs());
also_remove.push_back(getIsDefinedName(it->first)); also_remove.push_back(getIsDefinedName(it->first));
it->second->decvref(emitter); it->second->decvref(emitter);
...@@ -2208,6 +2217,8 @@ public: ...@@ -2208,6 +2217,8 @@ public:
return EndingState(st, phi_st, curblock); return EndingState(st, phi_st, curblock);
} }
// We have one successor, but they have more than one predecessor.
// We're going to sort out which symbols need to go in phi_st and which belong inst.
for (SymbolTable::iterator it = st->begin(); it != st->end();) { for (SymbolTable::iterator it = st->begin(); it != st->end();) {
if (allowableFakeEndingSymbol(it->first) || source->phis->isRequiredAfter(it->first, myblock)) { if (allowableFakeEndingSymbol(it->first) || source->phis->isRequiredAfter(it->first, myblock)) {
ASSERT(it->second->isGrabbed(), "%s", it->first.c_str()); ASSERT(it->second->isGrabbed(), "%s", it->first.c_str());
...@@ -2344,12 +2355,24 @@ public: ...@@ -2344,12 +2355,24 @@ public:
} }
void run(const CFGBlock* block) override { void run(const CFGBlock* block) override {
if (VERBOSITY("irgenerator") >= 1) { // print starting symbol table
printf(" %d init:", block->idx);
for (auto it = symbol_table.begin(); it != symbol_table.end(); ++it)
printf(" %s", it->first.c_str());
printf("\n");
}
for (int i = 0; i < block->body.size(); i++) { for (int i = 0; i < block->body.size(); i++) {
if (state == DEAD) if (state == DEAD)
break; break;
assert(state != FINISHED); assert(state != FINISHED);
doStmt(block->body[i], UnwindInfo(block->body[i], NULL)); doStmt(block->body[i], UnwindInfo(block->body[i], NULL));
} }
if (VERBOSITY("irgenerator") >= 1) { // print ending symbol table
printf(" %d fini:", block->idx);
for (auto it = symbol_table.begin(); it != symbol_table.end(); ++it)
printf(" %s", it->first.c_str());
printf("\n");
}
} }
void doSafePoint() override { emitter.getBuilder()->CreateCall(g.funcs.allowGLReadPreemption); } void doSafePoint() override { emitter.getBuilder()->CreateCall(g.funcs.allowGLReadPreemption); }
......
...@@ -95,10 +95,14 @@ public: ...@@ -95,10 +95,14 @@ public:
ParamNames* getParamNames() { return param_names; } ParamNames* getParamNames() { return param_names; }
}; };
// turns CFGBlocks into LLVM IR
class IRGenerator { class IRGenerator {
private: private:
public: public:
struct EndingState { struct EndingState {
// symbol_table records which Python variables are bound to what CompilerVariables at the end of this block.
// phi_symbol_table records the ones that will need to be `phi'd.
// both only record non-globals.
SymbolTable* symbol_table; SymbolTable* symbol_table;
ConcreteSymbolTable* phi_symbol_table; ConcreteSymbolTable* phi_symbol_table;
llvm::BasicBlock* ending_block; llvm::BasicBlock* ending_block;
...@@ -113,7 +117,7 @@ public: ...@@ -113,7 +117,7 @@ public:
virtual void giveLocalSymbol(InternedString name, CompilerVariable* var) = 0; virtual void giveLocalSymbol(InternedString name, CompilerVariable* var) = 0;
virtual void copySymbolsFrom(SymbolTable* st) = 0; virtual void copySymbolsFrom(SymbolTable* st) = 0;
virtual void run(const CFGBlock* block) = 0; virtual void run(const CFGBlock* block) = 0; // primary entry point
virtual EndingState getEndingSymbolTable() = 0; virtual EndingState getEndingSymbolTable() = 0;
virtual void doSafePoint() = 0; virtual void doSafePoint() = 0;
virtual void addFrameStackmapArgs(PatchpointInfo* pp, AST_stmt* current_stmt, virtual void addFrameStackmapArgs(PatchpointInfo* pp, AST_stmt* current_stmt,
......
...@@ -118,7 +118,7 @@ enum AST_TYPE { ...@@ -118,7 +118,7 @@ enum AST_TYPE {
DictComp = 15, DictComp = 15,
Set = 43, Set = 43,
Ellipsis = 87, Ellipsis = 87,
Expression = 88, Expression = 88, // like Module, but used for eval.
// Pseudo-nodes that are specific to this compiler: // Pseudo-nodes that are specific to this compiler:
Branch = 200, Branch = 200,
...@@ -1007,14 +1007,14 @@ class AST_LangPrimitive : public AST_expr { ...@@ -1007,14 +1007,14 @@ class AST_LangPrimitive : public AST_expr {
public: public:
enum Opcodes { enum Opcodes {
ISINSTANCE, ISINSTANCE,
LANDINGPAD, LANDINGPAD, // grabs the info about the last raised exception
LOCALS, LOCALS,
GET_ITER, GET_ITER,
IMPORT_FROM, IMPORT_FROM,
IMPORT_NAME, IMPORT_NAME,
IMPORT_STAR, IMPORT_STAR,
NONE, NONE,
NONZERO, NONZERO, // determines whether something is "true" for purposes of `if'
SET_EXC_INFO, SET_EXC_INFO,
UNCACHE_EXC_INFO, UNCACHE_EXC_INFO,
} opcode; } opcode;
......
This diff is collapsed.
...@@ -56,6 +56,7 @@ public: ...@@ -56,6 +56,7 @@ public:
void unconnectFrom(CFGBlock* successor); void unconnectFrom(CFGBlock* successor);
void push_back(AST_stmt* node) { body.push_back(node); } void push_back(AST_stmt* node) { body.push_back(node); }
void print();
}; };
// Control Flow Graph // Control Flow Graph
...@@ -79,6 +80,9 @@ public: ...@@ -79,6 +80,9 @@ public:
return block; return block;
} }
// Creates a block which must be placed later, using placeBlock().
// Must be placed on same CFG it was created on.
// You can also safely delete it without placing it.
CFGBlock* addDeferredBlock() { CFGBlock* addDeferredBlock() {
CFGBlock* block = new CFGBlock(this, -1); CFGBlock* block = new CFGBlock(this, -1);
return block; return block;
......
...@@ -39,6 +39,8 @@ ...@@ -39,6 +39,8 @@
#define _CAT(A, B) A##B #define _CAT(A, B) A##B
#define CAT(A, B) _CAT(A, B) #define CAT(A, B) _CAT(A, B)
#define ARRAY_LEN(arr) (sizeof(arr) / sizeof((arr)[0]))
// GCC and clang handle always_inline very differently; // GCC and clang handle always_inline very differently;
// we mostly only care about it for the stdlib, so just remove the attributes // we mostly only care about it for the stdlib, so just remove the attributes
// if we're not in clang // if we're not in clang
......
...@@ -52,7 +52,9 @@ bool endswith(const std::string& s, const std::string& pattern); ...@@ -52,7 +52,9 @@ bool endswith(const std::string& s, const std::string& pattern);
void removeDirectoryIfExists(const std::string& path); void removeDirectoryIfExists(const std::string& path);
template <class T1, class T2> void compareKeyset(T1* lhs, T2* rhs) { // Checks that lhs and rhs, which are iterables of InternedStrings, have the
// same set of names in them.
template <class T1, class T2> bool sameKeyset(T1* lhs, T2* rhs) {
std::vector<InternedString> lv, rv; std::vector<InternedString> lv, rv;
for (typename T1::iterator it = lhs->begin(); it != lhs->end(); it++) { for (typename T1::iterator it = lhs->begin(); it != lhs->end(); it++) {
lv.push_back(it->first); lv.push_back(it->first);
...@@ -89,7 +91,7 @@ template <class T1, class T2> void compareKeyset(T1* lhs, T2* rhs) { ...@@ -89,7 +91,7 @@ template <class T1, class T2> void compareKeyset(T1* lhs, T2* rhs) {
} }
good = false; good = false;
} }
assert(good); return good;
} }
} }
......
...@@ -366,6 +366,9 @@ std::string BoxedModule::name() { ...@@ -366,6 +366,9 @@ std::string BoxedModule::name() {
} }
} }
// This mustn't throw; our IR generator generates calls to it without "invoke" even when there are exception handlers /
// finally-blocks in scope.
// TODO: should we use C++11 `noexcept' here?
extern "C" Box* boxCLFunction(CLFunction* f, BoxedClosure* closure, bool isGenerator, extern "C" Box* boxCLFunction(CLFunction* f, BoxedClosure* closure, bool isGenerator,
std::initializer_list<Box*> defaults) { std::initializer_list<Box*> defaults) {
if (closure) if (closure)
......
# fail-if: '-n' in EXTRA_JIT_ARGS or '-O' in EXTRA_JIT_ARGS
# we have an llvm codegen bug that this file triggers when we JIT
class TestException(Exception): class TestException(Exception):
pass pass
......
# expected: fail
# allow-warning: converting unicode literal to str # allow-warning: converting unicode literal to str
import sys import sys
......
...@@ -27,5 +27,6 @@ def f2(): ...@@ -27,5 +27,6 @@ def f2():
print 'here' print 'here'
except: except:
print 'impossible' print 'impossible'
print D
raise raise
f2() f2()
# fail-if: (('-O' in EXTRA_JIT_ARGS) or ('-n' in EXTRA_JIT_ARGS)) and 'release' not in IMAGE
def f(): def f():
try: try:
def foo(): return 0 def foo(): return 0
......
# expected: fail # fail-if: (('-O' in EXTRA_JIT_ARGS) or ('-n' in EXTRA_JIT_ARGS)) and 'release' not in IMAGE
def f(): def f():
C = 23 C = 23
try: try:
......
...@@ -5,7 +5,7 @@ class Mgr(object): ...@@ -5,7 +5,7 @@ class Mgr(object):
def __enter__(self): def __enter__(self):
print 'Mgr.__enter__ accessed' print 'Mgr.__enter__ accessed'
def enterer(*args): def enterer(*args):
print 'Mgr.__enter__%r called' % (args,) print 'Mgr.__enter__ called'
return 23 return 23
return enterer return enterer
...@@ -13,7 +13,7 @@ class Mgr(object): ...@@ -13,7 +13,7 @@ class Mgr(object):
def __exit__(self): def __exit__(self):
print 'Mgr.__exit__ accessed' print 'Mgr.__exit__ accessed'
def exiter(*args): def exiter(*args):
print 'Mgr.__exit__%r called' % (args,) print 'Mgr.__exit__ called'
return False return False
return exiter return exiter
......
# fail-if: (('-O' in EXTRA_JIT_ARGS) or ('-n' in EXTRA_JIT_ARGS)) and 'release' not in IMAGE
def f(): def f():
# originally this exposed a bug in our irgen phase, so even `with None` # this exposes a bug in our irgen phase, so even `with None` bugs out here;
# failed here; the bug happened before actual execution. Just to test more # the bug happens before actual execution. Just to test more things, though,
# things, though, we use an actual contextmanager here. # we use an actual contextmanager here.
with open('/dev/null'): with open('/dev/null'):
def foo(): def foo():
# raises a syntaxerror
pass pass
f() f()
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