Commit 45225091 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Add a BoxedFrame* to the per-frame FrameInfo

This both lets us reuse the frame object and verify
that the BoxedFrame is still valid.
parent c40fc9ac
...@@ -862,6 +862,20 @@ void Assembler::je(JumpDestination dest) { ...@@ -862,6 +862,20 @@ void Assembler::je(JumpDestination dest) {
jmp_cond(dest, COND_EQUAL); jmp_cond(dest, COND_EQUAL);
} }
void Assembler::jmpq(Register dest) {
int reg_idx = dest.regnum;
if (reg_idx >= 8) {
emitRex(REX_B);
reg_idx -= 8;
}
assert(0 <= reg_idx && reg_idx < 8);
emitByte(0xff);
emitModRM(0b11, 0b100, reg_idx);
}
void Assembler::set_cond(Register reg, ConditionCode condition) { void Assembler::set_cond(Register reg, ConditionCode condition) {
......
...@@ -140,6 +140,7 @@ public: ...@@ -140,6 +140,7 @@ public:
void jmp_cond(JumpDestination dest, ConditionCode condition); void jmp_cond(JumpDestination dest, ConditionCode condition);
void jmp(JumpDestination dest); void jmp(JumpDestination dest);
void jmpq(Register dest);
void je(JumpDestination dest); void je(JumpDestination dest);
void jne(JumpDestination dest); void jne(JumpDestination dest);
......
...@@ -469,8 +469,8 @@ extern "C" PyObject* PyObject_CallObject(PyObject* obj, PyObject* args) noexcept ...@@ -469,8 +469,8 @@ extern "C" PyObject* PyObject_CallObject(PyObject* obj, PyObject* args) noexcept
r = runtimeCall(obj, ArgPassSpec(0, 0, false, false), NULL, NULL, NULL, NULL, NULL); r = runtimeCall(obj, ArgPassSpec(0, 0, false, false), NULL, NULL, NULL, NULL, NULL);
return r; return r;
} catch (ExcInfo e) { } catch (ExcInfo e) {
fatalOrError(PyExc_NotImplementedError, "unimplemented"); setCAPIException(e);
return nullptr; return NULL;
} }
} }
......
...@@ -100,6 +100,16 @@ static llvm::Value* getExcinfoGep(llvm::IRBuilder<true>& builder, llvm::Value* v ...@@ -100,6 +100,16 @@ static llvm::Value* getExcinfoGep(llvm::IRBuilder<true>& builder, llvm::Value* v
return builder.CreateConstInBoundsGEP2_32(builder.CreateConstInBoundsGEP2_32(v, 0, 0), 0, 0); return builder.CreateConstInBoundsGEP2_32(builder.CreateConstInBoundsGEP2_32(v, 0, 0), 0, 0);
} }
static llvm::Value* getFrameObjGep(llvm::IRBuilder<true>& builder, llvm::Value* v) {
static_assert(offsetof(FrameInfo, exc) == 0, "");
static_assert(sizeof(ExcInfo) == 24, "");
static_assert(sizeof(Box*) == 8, "");
static_assert(offsetof(FrameInfo, frame_obj) == 32, "");
return builder.CreateConstInBoundsGEP2_32(v, 0, 2);
// TODO: this could be made more resilient by doing something like
// gep->accumulateConstantOffset(g.tm->getDataLayout(), ap_offset)
}
llvm::Value* IRGenState::getFrameInfoVar() { llvm::Value* IRGenState::getFrameInfoVar() {
/* /*
There is a matrix of possibilities here. There is a matrix of possibilities here.
...@@ -162,6 +172,11 @@ llvm::Value* IRGenState::getFrameInfoVar() { ...@@ -162,6 +172,11 @@ llvm::Value* IRGenState::getFrameInfoVar() {
builder.CreateStore(this->boxed_locals, boxed_locals_gep); builder.CreateStore(this->boxed_locals, boxed_locals_gep);
} }
// frame_info.frame_obj = NULL
static llvm::Type* llvm_frame_obj_type_ptr
= llvm::cast<llvm::StructType>(g.llvm_frame_info_type)->getElementType(2);
builder.CreateStore(embedConstantPtr(NULL, llvm_frame_obj_type_ptr), getFrameObjGep(builder, al));
this->frame_info = al; this->frame_info = al;
} }
} }
......
...@@ -561,16 +561,29 @@ struct ExcInfo { ...@@ -561,16 +561,29 @@ struct ExcInfo {
void printExcAndTraceback() const; void printExcAndTraceback() const;
}; };
class BoxedFrame;
struct FrameInfo { struct FrameInfo {
// Note(kmod): we have a number of fields here that all have independent
// initialization rules. We could potentially save time on every function-entry
// by having an "initialized" variable (or condition) that guards all of them.
// *Not the same semantics as CPython's frame->f_exc* // *Not the same semantics as CPython's frame->f_exc*
// In CPython, f_exc is the saved exc_info from the previous frame. // In CPython, f_exc is the saved exc_info from the previous frame.
// In Pyston, exc is the frame-local value of sys.exc_info. // In Pyston, exc is the frame-local value of sys.exc_info.
// - This makes frame entering+leaving faster at the expense of slower exceptions. // - This makes frame entering+leaving faster at the expense of slower exceptions.
//
// exc.type is initialized to NULL at function entry, and exc.value and exc.tb are left
// uninitialized. When one wants to access any of the values, you need to check if exc.type
// is NULL, and if so crawl up the stack looking for the first frame with a non-null exc.type
// and copy that.
ExcInfo exc; ExcInfo exc;
// This field is always initialized:
Box* boxedLocals; Box* boxedLocals;
FrameInfo(ExcInfo exc) : exc(exc), boxedLocals(NULL) {} BoxedFrame* frame_obj;
FrameInfo(ExcInfo exc) : exc(exc), boxedLocals(NULL), frame_obj(0) {}
}; };
struct CallattrFlags { struct CallattrFlags {
......
...@@ -12,6 +12,9 @@ ...@@ -12,6 +12,9 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include "Python.h"
#include "pythread.h"
#include "codegen/unwinding.h" #include "codegen/unwinding.h"
#include "runtime/types.h" #include "runtime/types.h"
...@@ -19,14 +22,30 @@ namespace pyston { ...@@ -19,14 +22,30 @@ namespace pyston {
BoxedClass* frame_cls; BoxedClass* frame_cls;
// Issues:
// - breaks gdb backtraces
// - breaks c++ exceptions
// - we never free the trampolines
class BoxedFrame : public Box { class BoxedFrame : public Box {
public: public:
BoxedFrame() __attribute__((visibility("default"))) {} BoxedFrame(PythonFrameIterator&& it) __attribute__((visibility("default")))
: it(std::move(it)), thread_id(PyThread_get_thread_ident()) {}
PythonFrameIterator it;
long thread_id;
Box* _locals;
Box* _globals; Box* _globals;
Box* _code; Box* _code;
uint32_t _lineno;
void update() {
// This makes sense as an exception, but who knows how the user program would react
// (it might swallow it and do something different)
RELEASE_ASSERT(thread_id == PyThread_get_thread_ident(),
"frame objects can only be accessed from the same thread");
PythonFrameIterator new_it = it.getCurrentVersion();
RELEASE_ASSERT(new_it.exists() && new_it.getFrameInfo()->frame_obj == this, "frame has exited");
it = std::move(new_it);
}
// cpython frame objects have the following attributes // cpython frame objects have the following attributes
...@@ -61,10 +80,15 @@ public: ...@@ -61,10 +80,15 @@ public:
auto f = static_cast<BoxedFrame*>(b); auto f = static_cast<BoxedFrame*>(b);
v->visit(f->_code); v->visit(f->_code);
v->visit(f->_locals);
v->visit(f->_globals); v->visit(f->_globals);
} }
static void simpleDestructor(Box* b) {
auto f = static_cast<BoxedFrame*>(b);
f->it.~PythonFrameIterator();
}
static Box* code(Box* obj, void*) { static Box* code(Box* obj, void*) {
auto f = static_cast<BoxedFrame*>(obj); auto f = static_cast<BoxedFrame*>(obj);
return f->_code; return f->_code;
...@@ -72,7 +96,8 @@ public: ...@@ -72,7 +96,8 @@ public:
static Box* locals(Box* obj, void*) { static Box* locals(Box* obj, void*) {
auto f = static_cast<BoxedFrame*>(obj); auto f = static_cast<BoxedFrame*>(obj);
return f->_locals; f->update();
return f->it.fastLocalsToBoxedLocals();
} }
static Box* globals(Box* obj, void*) { static Box* globals(Box* obj, void*) {
...@@ -82,49 +107,43 @@ public: ...@@ -82,49 +107,43 @@ public:
static Box* lineno(Box* obj, void*) { static Box* lineno(Box* obj, void*) {
auto f = static_cast<BoxedFrame*>(obj); auto f = static_cast<BoxedFrame*>(obj);
return boxInt(f->_lineno); f->update();
std::unique_ptr<ExecutionPoint> fr = f->it.getExecutionPoint();
return boxInt(fr->current_stmt->lineno);
} }
DEFAULT_CLASS(frame_cls); DEFAULT_CLASS(frame_cls);
}; };
Box* getFrame(int depth) { Box* getFrame(int depth) {
std::unique_ptr<ExecutionPoint> fr = getExecutionPoint(depth); auto it = getPythonFrame(depth);
if (!fr) if (!it.exists())
return NULL; return NULL;
Box* locals = fastLocalsToBoxedLocals(depth); FrameInfo* fi = it.getFrameInfo();
if (fi->frame_obj == NULL) {
auto cf = it.getCF();
BoxedFrame* f = fi->frame_obj = new BoxedFrame(std::move(it));
assert(cf->clfunc->source->scoping->areGlobalsFromModule());
f->_globals = makeAttrWrapper(cf->clfunc->source->parent_module);
f->_code = codeForCLFunction(cf->clfunc);
}
BoxedFrame* rtn = new BoxedFrame(); return fi->frame_obj;
rtn->_locals = locals;
// basically the same as builtin globals()
// q: TODO is it ok that we don't return a real dict here?
rtn->_globals = makeAttrWrapper(fr->cf->clfunc->source->parent_module);
rtn->_code = codeForCLFunction(fr->cf->clfunc);
rtn->_lineno = fr->current_stmt->lineno;
return rtn;
} }
void setupFrame() { void setupFrame() {
frame_cls = BoxedHeapClass::create(type_cls, object_cls, &BoxedFrame::gchandler, 0, 0, sizeof(BoxedFrame), false, frame_cls = BoxedHeapClass::create(type_cls, object_cls, &BoxedFrame::gchandler, 0, 0, sizeof(BoxedFrame), false,
"frame"); "frame");
frame_cls->simple_destructor = BoxedFrame::simpleDestructor;
frame_cls->giveAttr("f_code", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedFrame::code, NULL, NULL)); frame_cls->giveAttr("f_code", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedFrame::code, NULL, NULL));
// until we can determine if there's code in the wild that depends
// on f_locals boxing up fast locals when it's called, add methods
// getLocals() and getLineno() that we can modify code to use.
#ifdef expose_f_locals
frame_cls->giveAttr("f_locals", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedFrame::locals, NULL, NULL)); frame_cls->giveAttr("f_locals", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedFrame::locals, NULL, NULL));
frame_cls->giveAttr("f_lineno", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedFrame::lineno, NULL, NULL)); frame_cls->giveAttr("f_lineno", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedFrame::lineno, NULL, NULL));
#else
frame_cls->giveAttr("getLocals", new BoxedFunction(boxRTFunction((void*)BoxedFrame::locals, UNKNOWN, 1)));
frame_cls->giveAttr("getLineno", new BoxedFunction(boxRTFunction((void*)BoxedFrame::lineno, BOXED_INT, 1)));
#endif
frame_cls->giveAttr("f_globals", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedFrame::globals, NULL, NULL)); frame_cls->giveAttr("f_globals", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedFrame::globals, NULL, NULL));
frame_cls->freeze(); frame_cls->freeze();
} }
} }
...@@ -158,6 +158,20 @@ static void writeTrivialEhFrame(void* eh_frame_addr, void* func_addr, uint64_t f ...@@ -158,6 +158,20 @@ static void writeTrivialEhFrame(void* eh_frame_addr, void* func_addr, uint64_t f
*size_ptr = func_size; *size_ptr = func_size;
} }
void EHFrameManager::writeAndRegister(void* func_addr, uint64_t func_size) {
assert(eh_frame_addr == NULL);
eh_frame_addr = malloc(EH_FRAME_SIZE);
writeTrivialEhFrame(eh_frame_addr, func_addr, func_size);
registerEHFrames((uint8_t*)eh_frame_addr, (uint64_t)eh_frame_addr, EH_FRAME_SIZE);
}
EHFrameManager::~EHFrameManager() {
if (eh_frame_addr) {
deregisterEHFrames((uint8_t*)eh_frame_addr, (uint64_t)eh_frame_addr, EH_FRAME_SIZE);
free(eh_frame_addr);
}
}
#if RUNTIMEICS_OMIT_FRAME_PTR #if RUNTIMEICS_OMIT_FRAME_PTR
// If you change this, you *must* update the value in _eh_frame_template // If you change this, you *must* update the value in _eh_frame_template
// (set the -9'th byte to this value plus 8) // (set the -9'th byte to this value plus 8)
...@@ -256,10 +270,7 @@ RuntimeIC::RuntimeIC(void* func_addr, int num_slots, int slot_size) { ...@@ -256,10 +270,7 @@ RuntimeIC::RuntimeIC(void* func_addr, int num_slots, int slot_size) {
// TODO: ideally would be more intelligent about allocation strategies. // TODO: ideally would be more intelligent about allocation strategies.
// The code sections should be together and the eh sections together // The code sections should be together and the eh sections together
eh_frame_addr = malloc(EH_FRAME_SIZE); eh_frame.writeAndRegister(addr, total_size);
writeTrivialEhFrame(eh_frame_addr, addr, total_size);
registerEHFrames((uint8_t*)eh_frame_addr, (uint64_t)eh_frame_addr, EH_FRAME_SIZE);
} else { } else {
addr = func_addr; addr = func_addr;
} }
...@@ -268,9 +279,7 @@ RuntimeIC::RuntimeIC(void* func_addr, int num_slots, int slot_size) { ...@@ -268,9 +279,7 @@ RuntimeIC::RuntimeIC(void* func_addr, int num_slots, int slot_size) {
RuntimeIC::~RuntimeIC() { RuntimeIC::~RuntimeIC() {
if (ENABLE_RUNTIME_ICS) { if (ENABLE_RUNTIME_ICS) {
deregisterCompiledPatchpoint(icinfo.get()); deregisterCompiledPatchpoint(icinfo.get());
deregisterEHFrames((uint8_t*)eh_frame_addr, (uint64_t)eh_frame_addr, EH_FRAME_SIZE);
free(addr); free(addr);
free(eh_frame_addr);
} else { } else {
} }
} }
......
...@@ -22,10 +22,20 @@ namespace pyston { ...@@ -22,10 +22,20 @@ namespace pyston {
class ICInfo; class ICInfo;
class EHFrameManager {
private:
void* eh_frame_addr;
public:
EHFrameManager() : eh_frame_addr(NULL) {}
~EHFrameManager();
void writeAndRegister(void* func_addr, uint64_t func_size);
};
class RuntimeIC { class RuntimeIC {
private: private:
void* addr; void* addr;
void* eh_frame_addr; EHFrameManager eh_frame;
std::unique_ptr<ICInfo> icinfo; std::unique_ptr<ICInfo> icinfo;
......
...@@ -15,7 +15,7 @@ d[2].append(3) ...@@ -15,7 +15,7 @@ d[2].append(3)
print sorted(d.items()) print sorted(d.items())
NT = collections.namedtuple("NT", ["field1", "field2"]) NT = collections.namedtuple("NT", ["field1", "field2"])
print NT.__name__ print NT.__name__, NT
n = NT(1, "hi") n = NT(1, "hi")
print n.field1, n.field2, len(n), list(n), n[0], n[-1] print n.field1, n.field2, len(n), list(n), n[0], n[-1]
print n print n
# expected: fail
# - needs sys._getframe
import collections
NT = collections.namedtuple("NT", ["field1", "field2"])
print NT
...@@ -5,15 +5,9 @@ Frame Hack Recipe #1: Ruby-style string interpolation (version 1) ...@@ -5,15 +5,9 @@ Frame Hack Recipe #1: Ruby-style string interpolation (version 1)
import sys import sys
from string import Template from string import Template
def getLocals(frame):
try:
return frame.f_locals
except AttributeError:
return frame.getLocals()
def interpolate(templateStr): def interpolate(templateStr):
frame = sys._getframe(1) frame = sys._getframe(1)
framedict = getLocals(frame) framedict = frame.f_locals
t = Template(templateStr) t = Template(templateStr)
return t.substitute(**framedict) return t.substitute(**framedict)
......
import sys
def f():
fr = sys._getframe(0)
print fr.f_lineno
print fr.f_lineno
print sorted(fr.f_locals.keys())
a = 1
print sorted(fr.f_locals.keys())
f()
assert sys._getframe(0) is sys._getframe(0)
def f2():
f1 = sys._getframe(0)
# trigger osr:
for i in xrange(20000):
pass
assert f1 is sys._getframe(0)
f2()
# Make sure we can throw exceptions through frame we called _getframe
import sys
def g():
sys._getframe(1)
1/0
def f():
g()
try:
f()
except Exception as e:
print e
# expected: fail
# - we don't (yet?) support looking at frame objects after
# their frame has exited
import sys
def f():
return sys._getframe(0)
fr = f()
print fr.f_locals
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