Commit 41c0273e authored by Kevin Modzelewski's avatar Kevin Modzelewski

Get rid of "interpreted CompiledFunctions"

This was vistigial from the old llvm interpreter, where we it made a bit more
sense.  Now it's just weird, and caused the tiering logic to be spread into
weird places.  It was also the source of some bugs, since when we would deopt
there's not really any relevant CompiledFunction that represents the deopt
frame (this is why type speculation had to be temporarily disabled).

So instead, make it so that the interpreter (and by extension, the baseline
jit) work on the CLFunction directly, since the CompiledFunction didn't hold
any relevant information anyway.  This require a whole bunch of refactoring and
API changes.

This commit doesn't actually change any of the tiering decisions (I think), but
should just make them clearer; the actual behavioral changes will be done in
upcoming commits.
parent fc238ed8
This diff is collapsed.
......@@ -29,7 +29,7 @@ class AST_Jump;
class Box;
class BoxedClosure;
class BoxedDict;
struct CompiledFunction;
struct CLFunction;
struct LineInfo;
extern const void* interpreter_instr_addr;
......@@ -72,15 +72,15 @@ struct Value {
};
void setupInterpreter();
Box* astInterpretFunction(CompiledFunction* f, int nargs, Box* closure, Box* generator, Box* globals, Box* arg1,
Box* arg2, Box* arg3, Box** args);
Box* astInterpretFunctionEval(CompiledFunction* cf, Box* globals, Box* boxedLocals);
Box* astInterpretFrom(CompiledFunction* cf, AST_expr* after_expr, AST_stmt* enclosing_stmt, Box* expr_val,
Box* astInterpretFunction(CLFunction* f, int nargs, Box* closure, Box* generator, Box* globals, Box* arg1, Box* arg2,
Box* arg3, Box** args);
Box* astInterpretFunctionEval(CLFunction* cf, Box* globals, Box* boxedLocals);
Box* astInterpretFrom(CLFunction* cf, AST_expr* after_expr, AST_stmt* enclosing_stmt, Box* expr_val,
FrameStackState frame_state);
AST_stmt* getCurrentStatementForInterpretedFrame(void* frame_ptr);
Box* getGlobalsForInterpretedFrame(void* frame_ptr);
CompiledFunction* getCFForInterpretedFrame(void* frame_ptr);
CLFunction* getCLForInterpretedFrame(void* frame_ptr);
struct FrameInfo;
FrameInfo* getFrameInfoForInterpretedFrame(void* frame_ptr);
BoxedClosure* passedClosureForInterpretedFrame(void* frame_ptr);
......
......@@ -27,6 +27,7 @@
#include "analysis/function_analysis.h"
#include "analysis/scoping_analysis.h"
#include "codegen/baseline_jit.h"
#include "codegen/compvars.h"
#include "core/ast.h"
#include "core/util.h"
......@@ -35,6 +36,27 @@ namespace pyston {
DS_DEFINE_RWLOCK(codegen_rwlock);
CLFunction::CLFunction(int num_args, int num_defaults, bool takes_varargs, bool takes_kwargs,
std::unique_ptr<SourceInfo> source)
: paramspec(num_args, num_defaults, takes_varargs, takes_kwargs),
source(std::move(source)),
param_names(this->source->ast, this->source->getInternedStrings()),
always_use_version(NULL),
code_obj(NULL),
times_interpreted(0) {
assert(num_args >= num_defaults);
}
CLFunction::CLFunction(int num_args, int num_defaults, bool takes_varargs, bool takes_kwargs,
const ParamNames& param_names)
: paramspec(num_args, num_defaults, takes_varargs, takes_kwargs),
source(nullptr),
param_names(param_names),
always_use_version(NULL),
code_obj(NULL),
times_interpreted(0) {
assert(num_args >= num_defaults);
}
SourceInfo::SourceInfo(BoxedModule* m, ScopingAnalysis* scoping, FutureFlags future_flags, AST* ast,
std::vector<AST_stmt*> body, std::string fn)
: parent_module(m),
......
......@@ -249,7 +249,7 @@ public:
// var->getValue()->dump(); llvm::errs() << '\n';
// ptr->dump(); llvm::errs() << '\n';
// converted->getValue()->dump(); llvm::errs() << '\n';
bool do_patchpoint = ENABLE_ICSETATTRS && !info.isInterpreted();
bool do_patchpoint = ENABLE_ICSETATTRS;
if (do_patchpoint) {
ICSetupInfo* pp = createSetattrIC(info.getTypeRecorder());
......@@ -269,7 +269,7 @@ public:
llvm::Constant* ptr = embedRelocatablePtr(attr, g.llvm_boxedstring_type_ptr);
// TODO
// bool do_patchpoint = ENABLE_ICDELATTRS && !info.isInterpreted();
// bool do_patchpoint = ENABLE_ICDELATTRS;
bool do_patchpoint = false;
if (do_patchpoint) {
......@@ -317,7 +317,7 @@ public:
}
ConcreteCompilerVariable* len(IREmitter& emitter, const OpInfo& info, ConcreteCompilerVariable* var) override {
bool do_patchpoint = ENABLE_ICGENERICS && !info.isInterpreted();
bool do_patchpoint = ENABLE_ICGENERICS;
llvm::Value* rtn;
if (do_patchpoint) {
ICSetupInfo* pp = createGenericIC(info.getTypeRecorder(), true, 256);
......@@ -337,7 +337,7 @@ public:
CompilerVariable* slice) override {
ConcreteCompilerVariable* converted_slice = slice->makeConverted(emitter, slice->getBoxType());
bool do_patchpoint = ENABLE_ICGETITEMS && !info.isInterpreted();
bool do_patchpoint = ENABLE_ICGETITEMS;
llvm::Value* rtn;
if (do_patchpoint) {
ICSetupInfo* pp = createGetitemIC(info.getTypeRecorder());
......@@ -414,7 +414,7 @@ public:
ConcreteCompilerVariable* converted_rhs = rhs->makeConverted(emitter, rhs->getBoxType());
llvm::Value* rtn;
bool do_patchpoint = ENABLE_ICBINEXPS && !info.isInterpreted();
bool do_patchpoint = ENABLE_ICBINEXPS;
llvm::Value* rt_func;
void* rt_func_addr;
......@@ -495,7 +495,7 @@ CompilerVariable* UnknownType::getattr(IREmitter& emitter, const OpInfo& info, C
raw_func = (void*)pyston::getattr;
}
bool do_patchpoint = ENABLE_ICGETATTRS && !info.isInterpreted();
bool do_patchpoint = ENABLE_ICGETATTRS;
if (do_patchpoint) {
ICSetupInfo* pp = createGetattrIC(info.getTypeRecorder());
......@@ -551,19 +551,13 @@ static ConcreteCompilerVariable* _call(IREmitter& emitter, const OpInfo& info, l
if (args.size() >= 4) {
llvm::Value* arg_array;
if (info.isInterpreted()) {
llvm::Value* n_bytes = getConstantInt((args.size() - 3) * sizeof(Box*), g.i64);
mallocsave = emitter.getBuilder()->CreateCall(g.funcs.malloc, n_bytes);
arg_array = emitter.getBuilder()->CreateBitCast(mallocsave, g.llvm_value_type_ptr->getPointerTo());
} else {
llvm::Value* n_varargs = getConstantInt(args.size() - 3, g.i64);
llvm::Value* n_varargs = getConstantInt(args.size() - 3, g.i64);
// Don't use the IRBuilder since we want to specifically put this in the entry block so it only gets called
// once.
// TODO we could take this further and use the same alloca for all function calls?
llvm::Instruction* insertion_point = emitter.currentFunction()->func->getEntryBlock().getFirstInsertionPt();
arg_array = new llvm::AllocaInst(g.llvm_value_type_ptr, n_varargs, "arg_scratch", insertion_point);
}
// Don't use the IRBuilder since we want to specifically put this in the entry block so it only gets called
// once.
// TODO we could take this further and use the same alloca for all function calls?
llvm::Instruction* insertion_point = emitter.currentFunction()->func->getEntryBlock().getFirstInsertionPt();
arg_array = new llvm::AllocaInst(g.llvm_value_type_ptr, n_varargs, "arg_scratch", insertion_point);
for (int i = 3; i < args.size(); i++) {
llvm::Value* ptr = emitter.getBuilder()->CreateConstGEP1_32(arg_array, i - 3);
......@@ -590,8 +584,7 @@ static ConcreteCompilerVariable* _call(IREmitter& emitter, const OpInfo& info, l
// for (auto a : llvm_args)
// a->dump();
bool do_patchpoint = ENABLE_ICCALLSITES && !info.isInterpreted()
&& (func_addr == runtimeCall || func_addr == pyston::callattr);
bool do_patchpoint = ENABLE_ICCALLSITES && (func_addr == runtimeCall || func_addr == pyston::callattr);
if (do_patchpoint) {
assert(func_addr);
......@@ -685,7 +678,7 @@ CompilerVariable* UnknownType::callattr(IREmitter& emitter, const OpInfo& info,
}
ConcreteCompilerVariable* UnknownType::nonzero(IREmitter& emitter, const OpInfo& info, ConcreteCompilerVariable* var) {
bool do_patchpoint = ENABLE_ICNONZEROS && !info.isInterpreted();
bool do_patchpoint = ENABLE_ICNONZEROS;
llvm::Value* rtn_val;
if (do_patchpoint) {
ICSetupInfo* pp = createNonzeroIC(info.getTypeRecorder());
......@@ -702,7 +695,7 @@ ConcreteCompilerVariable* UnknownType::nonzero(IREmitter& emitter, const OpInfo&
}
ConcreteCompilerVariable* UnknownType::hasnext(IREmitter& emitter, const OpInfo& info, ConcreteCompilerVariable* var) {
bool do_patchpoint = ENABLE_ICS && !info.isInterpreted();
bool do_patchpoint = ENABLE_ICS;
do_patchpoint = false; // we are currently using runtime ics for this
llvm::Value* rtn_val;
if (do_patchpoint) {
......@@ -1573,7 +1566,6 @@ public:
}
assert(found);
assert(!cf->is_interpreted);
assert(cf->code);
std::vector<llvm::Type*> arg_types;
......
......@@ -937,16 +937,15 @@ static std::string getUniqueFunctionName(std::string nameprefix, EffortLevel eff
os << "_e" << (int)effort;
if (entry) {
os << "_osr" << entry->backedge->target->idx;
if (entry->cf->func)
os << "_from_" << entry->cf->func->getName().data();
}
os << '_' << num_functions;
num_functions++;
return os.str();
}
CompiledFunction* doCompile(SourceInfo* source, ParamNames* param_names, const OSREntryDescriptor* entry_descriptor,
EffortLevel effort, FunctionSpecialization* spec, std::string nameprefix) {
CompiledFunction* doCompile(CLFunction* clfunc, SourceInfo* source, ParamNames* param_names,
const OSREntryDescriptor* entry_descriptor, EffortLevel effort,
FunctionSpecialization* spec, std::string nameprefix) {
Timer _t("in doCompile");
Timer _t2;
long irgen_us = 0;
......@@ -1010,8 +1009,7 @@ CompiledFunction* doCompile(SourceInfo* source, ParamNames* param_names, const O
}
CompiledFunction* cf
= new CompiledFunction(NULL, spec, (effort == EffortLevel::INTERPRETED), NULL, effort, entry_descriptor);
CompiledFunction* cf = new CompiledFunction(NULL, spec, NULL, effort, entry_descriptor);
// Make sure that the instruction memory keeps the module object alive.
// TODO: implement this for real
......@@ -1061,7 +1059,7 @@ CompiledFunction* doCompile(SourceInfo* source, ParamNames* param_names, const O
else
phis = computeRequiredPhis(*param_names, source->cfg, liveness, source->getScopeInfo());
IRGenState irstate(cf, source, std::move(phis), param_names, getGCBuilder(), dbg_funcinfo);
IRGenState irstate(clfunc, cf, source, std::move(phis), param_names, getGCBuilder(), dbg_funcinfo);
emitBBs(&irstate, types, entry_descriptor, blocks);
......
......@@ -100,8 +100,9 @@ extern const std::string PASSED_GENERATOR_NAME;
InternedString getIsDefinedName(InternedString name, InternedStringPool& interned_strings);
bool isIsDefinedName(llvm::StringRef name);
CompiledFunction* doCompile(SourceInfo* source, ParamNames* param_names, const OSREntryDescriptor* entry_descriptor,
EffortLevel effort, FunctionSpecialization* spec, std::string nameprefix);
CompiledFunction* doCompile(CLFunction* clfunc, SourceInfo* source, ParamNames* param_names,
const OSREntryDescriptor* entry_descriptor, EffortLevel effort,
FunctionSpecialization* spec, std::string nameprefix);
// A common pattern is to branch based off whether a variable is defined but only if it is
// potentially-undefined. If it is potentially-undefined, we have to generate control-flow
......@@ -134,7 +135,6 @@ public:
OpInfo(EffortLevel effort, TypeRecorder* type_recorder, UnwindInfo unw_info)
: effort(effort), type_recorder(type_recorder), unw_info(unw_info) {}
bool isInterpreted() const { return effort == EffortLevel::INTERPRETED; }
TypeRecorder* getTypeRecorder() const { return type_recorder; }
};
}
......
This diff is collapsed.
......@@ -42,9 +42,11 @@ extern "C" void dumpLLVM(llvm::Value* v) {
v->dump();
}
IRGenState::IRGenState(CompiledFunction* cf, SourceInfo* source_info, std::unique_ptr<PhiAnalysis> phis,
ParamNames* param_names, GCBuilder* gc, llvm::MDNode* func_dbg_info)
: cf(cf),
IRGenState::IRGenState(CLFunction* clfunc, CompiledFunction* cf, SourceInfo* source_info,
std::unique_ptr<PhiAnalysis> phis, ParamNames* param_names, GCBuilder* gc,
llvm::MDNode* func_dbg_info)
: clfunc(clfunc),
cf(cf),
source_info(source_info),
phis(std::move(phis)),
param_names(param_names),
......@@ -402,8 +404,6 @@ public:
llvm::Value* createIC(const ICSetupInfo* pp, void* func_addr, const std::vector<llvm::Value*>& args,
UnwindInfo unw_info) override {
assert(irstate->getEffortLevel() != EffortLevel::INTERPRETED);
std::vector<llvm::Value*> stackmap_args;
llvm::CallSite rtn
......@@ -493,7 +493,7 @@ private:
assert(ast);
EffortLevel effort = irstate->getEffortLevel();
bool record_types = (effort != EffortLevel::INTERPRETED && effort != EffortLevel::MAXIMAL);
bool record_types = effort != EffortLevel::MAXIMAL;
TypeRecorder* type_recorder;
if (record_types) {
......@@ -591,38 +591,29 @@ private:
llvm::Value* cxaexc_pointer = emitter.getBuilder()->CreateExtractValue(landing_pad, { 0 });
if (irstate->getEffortLevel() != EffortLevel::INTERPRETED) {
llvm::Function* std_module_catch = g.stdlib_module->getFunction("__cxa_begin_catch");
auto begin_catch_func = g.cur_module->getOrInsertFunction(std_module_catch->getName(),
std_module_catch->getFunctionType());
assert(begin_catch_func);
llvm::Value* excinfo_pointer = emitter.getBuilder()->CreateCall(begin_catch_func, cxaexc_pointer);
llvm::Value* excinfo_pointer_casted
= emitter.getBuilder()->CreateBitCast(excinfo_pointer, g.llvm_excinfo_type->getPointerTo());
auto* builder = emitter.getBuilder();
llvm::Value* exc_type
= builder->CreateLoad(builder->CreateConstInBoundsGEP2_32(excinfo_pointer_casted, 0, 0));
llvm::Value* exc_value
= builder->CreateLoad(builder->CreateConstInBoundsGEP2_32(excinfo_pointer_casted, 0, 1));
llvm::Value* exc_traceback
= builder->CreateLoad(builder->CreateConstInBoundsGEP2_32(excinfo_pointer_casted, 0, 2));
assert(exc_type->getType() == g.llvm_value_type_ptr);
assert(exc_value->getType() == g.llvm_value_type_ptr);
assert(exc_traceback->getType() == g.llvm_value_type_ptr);
return makeTuple({ new ConcreteCompilerVariable(UNKNOWN, exc_type, true),
new ConcreteCompilerVariable(UNKNOWN, exc_value, true),
new ConcreteCompilerVariable(UNKNOWN, exc_traceback, true) });
} else {
// TODO This doesn't get hit, right?
abort();
llvm::Function* std_module_catch = g.stdlib_module->getFunction("__cxa_begin_catch");
auto begin_catch_func = g.cur_module->getOrInsertFunction(std_module_catch->getName(),
std_module_catch->getFunctionType());
assert(begin_catch_func);
// The interpreter can't really support the full C++ exception handling model since it's
// itself written in C++. Let's make it easier for the interpreter and use a simpler interface:
llvm::Value* exc_obj = emitter.getBuilder()->CreateBitCast(cxaexc_pointer, g.llvm_value_type_ptr);
}
llvm::Value* excinfo_pointer = emitter.getBuilder()->CreateCall(begin_catch_func, cxaexc_pointer);
llvm::Value* excinfo_pointer_casted
= emitter.getBuilder()->CreateBitCast(excinfo_pointer, g.llvm_excinfo_type->getPointerTo());
auto* builder = emitter.getBuilder();
llvm::Value* exc_type
= builder->CreateLoad(builder->CreateConstInBoundsGEP2_32(excinfo_pointer_casted, 0, 0));
llvm::Value* exc_value
= builder->CreateLoad(builder->CreateConstInBoundsGEP2_32(excinfo_pointer_casted, 0, 1));
llvm::Value* exc_traceback
= builder->CreateLoad(builder->CreateConstInBoundsGEP2_32(excinfo_pointer_casted, 0, 2));
assert(exc_type->getType() == g.llvm_value_type_ptr);
assert(exc_value->getType() == g.llvm_value_type_ptr);
assert(exc_traceback->getType() == g.llvm_value_type_ptr);
return makeTuple({ new ConcreteCompilerVariable(UNKNOWN, exc_type, true),
new ConcreteCompilerVariable(UNKNOWN, exc_value, true),
new ConcreteCompilerVariable(UNKNOWN, exc_traceback, true) });
}
case AST_LangPrimitive::LOCALS: {
return new ConcreteCompilerVariable(UNKNOWN, irstate->getBoxedLocalsVar(), true);
......@@ -983,7 +974,7 @@ private:
if (node->id.s() == "None")
return getNone();
bool do_patchpoint = ENABLE_ICGETGLOBALS && (irstate->getEffortLevel() != EffortLevel::INTERPRETED);
bool do_patchpoint = ENABLE_ICGETGLOBALS;
if (do_patchpoint) {
ICSetupInfo* pp = createGetGlobalIC(getOpInfoForNode(node, unw_info).getTypeRecorder());
......@@ -1654,7 +1645,7 @@ private:
// TODO add a CompilerVariable::setattr, which can (similar to getitem)
// statically-resolve the function if possible, and only fall back to
// patchpoints if it couldn't.
bool do_patchpoint = ENABLE_ICSETITEMS && (irstate->getEffortLevel() != EffortLevel::INTERPRETED);
bool do_patchpoint = ENABLE_ICSETITEMS;
if (do_patchpoint) {
ICSetupInfo* pp = createSetitemIC(getEmptyOpInfo(unw_info).getTypeRecorder());
......@@ -1780,7 +1771,7 @@ private:
tget->decvref(emitter);
slice->decvref(emitter);
bool do_patchpoint = ENABLE_ICDELITEMS && (irstate->getEffortLevel() != EffortLevel::INTERPRETED);
bool do_patchpoint = ENABLE_ICDELITEMS;
if (do_patchpoint) {
ICSetupInfo* pp = createDelitemIC(getEmptyOpInfo(unw_info).getTypeRecorder());
......@@ -2042,8 +2033,8 @@ private:
// Emitting the actual OSR:
emitter.getBuilder()->SetInsertPoint(onramp);
OSREntryDescriptor* entry = OSREntryDescriptor::create(irstate->getCurFunction(), osr_key);
OSRExit* exit = new OSRExit(irstate->getCurFunction(), entry);
OSREntryDescriptor* entry = OSREntryDescriptor::create(irstate->getCL(), osr_key);
OSRExit* exit = new OSRExit(entry);
llvm::Value* partial_func = emitter.getBuilder()->CreateCall(g.funcs.compilePartialFunc,
embedRelocatablePtr(exit, g.i8->getPointerTo()));
......
......@@ -54,6 +54,9 @@ extern const std::string FRAME_INFO_PTR_NAME;
// TODO this probably shouldn't be here
class IRGenState {
private:
// Note: due to some not-yet-fixed behavior, cf->clfunc is NULL will only get set to point
// to clfunc at the end of irgen.
CLFunction* clfunc;
CompiledFunction* cf;
SourceInfo* source_info;
std::unique_ptr<PhiAnalysis> phis;
......@@ -69,11 +72,12 @@ private:
public:
IRGenState(CompiledFunction* cf, SourceInfo* source_info, std::unique_ptr<PhiAnalysis> phis,
IRGenState(CLFunction* clfunc, CompiledFunction* cf, SourceInfo* source_info, std::unique_ptr<PhiAnalysis> phis,
ParamNames* param_names, GCBuilder* gc, llvm::MDNode* func_dbg_info);
~IRGenState();
CompiledFunction* getCurFunction() { return cf; }
CLFunction* getCL() { return clfunc; }
llvm::Function* getLLVMFunction() { return cf->func; }
......
......@@ -31,26 +31,25 @@ struct StackMap;
class OSREntryDescriptor {
private:
OSREntryDescriptor(CompiledFunction* from_cf, AST_Jump* backedge) : cf(from_cf), backedge(backedge) {}
OSREntryDescriptor(CLFunction* clfunc, AST_Jump* backedge) : clfunc(clfunc), backedge(backedge) { assert(clfunc); }
public:
CompiledFunction* const cf;
CLFunction* clfunc;
AST_Jump* const backedge;
typedef std::map<InternedString, ConcreteCompilerType*> ArgMap;
ArgMap args;
static OSREntryDescriptor* create(CompiledFunction* from_cf, AST_Jump* backedge) {
return new OSREntryDescriptor(from_cf, backedge);
static OSREntryDescriptor* create(CLFunction* clfunc, AST_Jump* backedge) {
return new OSREntryDescriptor(clfunc, backedge);
}
};
class OSRExit {
private:
public:
CompiledFunction* const parent_cf;
const OSREntryDescriptor* entry;
OSRExit(CompiledFunction* parent_cf, const OSREntryDescriptor* entry) : parent_cf(parent_cf), entry(entry) {}
OSRExit(const OSREntryDescriptor* entry) : entry(entry) {}
};
}
......
......@@ -143,15 +143,6 @@ static std::unordered_set<int> extractLiveOuts(StackMap::Record* r, llvm::Callin
}
void processStackmap(CompiledFunction* cf, StackMap* stackmap) {
// FIXME: this is pretty hacky, that we don't delete the patchpoints here.
// We need them currently for the llvm interpreter.
// Eventually we'll get rid of that and use an AST interpreter, and then we won't need this hack.
if (cf->effort == EffortLevel::INTERPRETED) {
assert(!stackmap);
new_patchpoints.clear();
return;
}
int nrecords = stackmap ? stackmap->records.size() : 0;
assert(cf->location_map == NULL);
......
......@@ -276,6 +276,9 @@ struct PythonFrameId {
class PythonFrameIteratorImpl {
public:
PythonFrameId id;
CLFunction* cl; // always exists
// These only exist if id.type==COMPILED:
CompiledFunction* cf;
// We have to save a copy of the regs since it's very difficult to keep the unw_context_t
// structure valid.
......@@ -284,15 +287,26 @@ public:
PythonFrameIteratorImpl() : regs_valid(0) {}
PythonFrameIteratorImpl(PythonFrameId::FrameType type, uint64_t ip, uint64_t bp, CompiledFunction* cf)
: id(PythonFrameId(type, ip, bp)), cf(cf), regs_valid(0) {}
PythonFrameIteratorImpl(PythonFrameId::FrameType type, uint64_t ip, uint64_t bp, CLFunction* cl,
CompiledFunction* cf)
: id(PythonFrameId(type, ip, bp)), cl(cl), cf(cf), regs_valid(0) {
assert(cl);
assert((type == PythonFrameId::COMPILED) == (cf != NULL));
}
CompiledFunction* getCF() const {
assert(cf);
return cf;
}
CLFunction* getCL() const {
assert(cl);
return cl;
}
uint64_t readLocation(const StackMap::Record::Location& loc) {
assert(id.type == PythonFrameId::COMPILED);
if (loc.type == StackMap::Record::Location::LocationType::Register) {
// TODO: need to make sure we deal with patchpoints appropriately
return getReg(loc.regnum);
......@@ -412,6 +426,11 @@ static bool inGeneratorEntry(unw_word_t ip) {
return ((unw_word_t)generatorEntry < ip && ip <= generator_entry_end);
}
static bool inDeopt(unw_word_t ip) {
static unw_word_t deopt_end = getFunctionEnd((unw_word_t)deopt);
return ((unw_word_t)deopt < ip && ip <= deopt_end);
}
static inline unw_word_t get_cursor_reg(unw_cursor_t* cursor, int reg) {
unw_word_t v;
......@@ -430,18 +449,16 @@ static inline unw_word_t get_cursor_bp(unw_cursor_t* cursor) {
// frame information through the PythonFrameIteratorImpl* info arg.
bool frameIsPythonFrame(unw_word_t ip, unw_word_t bp, unw_cursor_t* cursor, PythonFrameIteratorImpl* info) {
CompiledFunction* cf = getCFForAddress(ip);
CLFunction* cl = cf ? cf->clfunc : NULL;
bool jitted = cf != NULL;
if (!cf) {
if (inASTInterpreterExecuteInner(ip)) {
cf = getCFForInterpretedFrame((void*)bp);
assert(cf);
}
}
bool interpreted = !jitted && inASTInterpreterExecuteInner(ip);
if (interpreted)
cl = getCLForInterpretedFrame((void*)bp);
if (!cf)
if (!jitted && !interpreted)
return false;
*info = PythonFrameIteratorImpl(jitted ? PythonFrameId::COMPILED : PythonFrameId::INTERPRETED, ip, bp, cf);
*info = PythonFrameIteratorImpl(jitted ? PythonFrameId::COMPILED : PythonFrameId::INTERPRETED, ip, bp, cl, cf);
if (jitted) {
// Try getting all the callee-save registers, and save the ones we were able to get.
// Some of them may be inaccessible, I think because they weren't defined by that
......@@ -576,10 +593,10 @@ void throwingException(PythonUnwindSession* unwind) {
static const LineInfo lineInfoForFrame(PythonFrameIteratorImpl* frame_it) {
AST_stmt* current_stmt = frame_it->getCurrentStatement();
auto* cf = frame_it->getCF();
assert(cf);
auto* cl = frame_it->getCL();
assert(cl);
auto source = cf->clfunc->source.get();
auto source = cl->source.get();
return LineInfo(current_stmt->lineno, current_stmt->col_offset, source->fn, source->getName());
}
......@@ -609,12 +626,16 @@ void unwindingThroughFrame(PythonUnwindSession* unwind_session, unw_cursor_t* cu
unw_word_t bp = get_cursor_bp(cursor);
PythonFrameIteratorImpl frame_iter;
if (frameIsPythonFrame(ip, bp, cursor, &frame_iter)) {
if (inDeopt(ip)) {
assert(!unwind_session->shouldSkipFrame());
unwind_session->setShouldSkipNextFrame(true);
} else if (frameIsPythonFrame(ip, bp, cursor, &frame_iter)) {
if (!unwind_session->shouldSkipFrame())
unwind_session->addTraceback(lineInfoForFrame(&frame_iter));
// frame_iter->cf->entry_descriptor will be non-null for OSR frames.
unwind_session->setShouldSkipNextFrame((bool)frame_iter.cf->entry_descriptor);
bool was_osr = (frame_iter.getId().type == PythonFrameId::COMPILED) && (frame_iter.cf->entry_descriptor);
unwind_session->setShouldSkipNextFrame(was_osr);
}
}
......@@ -641,16 +662,21 @@ template <typename Func> void unwindPythonStack(Func func) {
unw_word_t ip = get_cursor_ip(&cursor);
unw_word_t bp = get_cursor_bp(&cursor);
// TODO: this should probably just call unwindingThroughFrame?
bool stop_unwinding = false;
PythonFrameIteratorImpl frame_iter;
if (frameIsPythonFrame(ip, bp, &cursor, &frame_iter)) {
if (inDeopt(ip)) {
assert(!unwind_session->shouldSkipFrame());
unwind_session->setShouldSkipNextFrame(true);
} else if (frameIsPythonFrame(ip, bp, &cursor, &frame_iter)) {
if (!unwind_session->shouldSkipFrame())
stop_unwinding = func(&frame_iter);
// frame_iter->cf->entry_descriptor will be non-null for OSR frames.
unwind_session->setShouldSkipNextFrame((bool)frame_iter.cf->entry_descriptor);
bool was_osr = (frame_iter.getId().type == PythonFrameId::COMPILED) && (frame_iter.cf->entry_descriptor);
unwind_session->setShouldSkipNextFrame(was_osr);
}
if (stop_unwinding)
......@@ -791,11 +817,11 @@ ExcInfo* getFrameExcInfo() {
return cur_exc;
}
CompiledFunction* getTopCompiledFunction() {
CLFunction* getTopPythonFunction() {
auto rtn = getTopPythonFrame();
if (!rtn)
return NULL;
return getTopPythonFrame()->getCF();
return getTopPythonFrame()->getCL();
}
Box* getGlobals() {
......@@ -810,10 +836,10 @@ Box* getGlobalsDict() {
}
BoxedModule* getCurrentModule() {
CompiledFunction* compiledFunction = getTopCompiledFunction();
if (!compiledFunction)
CLFunction* clfunc = getTopPythonFunction();
if (!clfunc)
return NULL;
return compiledFunction->clfunc->source->parent_module;
return clfunc->source->parent_module;
}
PythonFrameIterator getPythonFrame(int depth) {
......@@ -844,11 +870,11 @@ PythonFrameIterator::PythonFrameIterator(std::unique_ptr<PythonFrameIteratorImpl
std::swap(this->impl, impl);
}
// TODO factor getStackLocalsIncludingUserHidden and fastLocalsToBoxedLocals
// TODO factor getDeoptState and fastLocalsToBoxedLocals
// because they are pretty ugly but have a pretty repetitive pattern.
FrameStackState getFrameStackState() {
FrameStackState rtn(NULL, NULL);
DeoptState getDeoptState() {
DeoptState rtn;
bool found = false;
unwindPythonStack([&](PythonFrameIteratorImpl* frame_iter) {
BoxedDict* d;
......@@ -917,7 +943,9 @@ FrameStackState getFrameStackState() {
abort();
}
rtn = FrameStackState(d, frame_iter->getFrameInfo());
rtn.frame_state = FrameStackState(d, frame_iter->getFrameInfo());
rtn.cf = cf;
rtn.current_stmt = frame_iter->getCurrentStatement();
found = true;
return true;
});
......@@ -937,17 +965,18 @@ Box* PythonFrameIterator::fastLocalsToBoxedLocals() {
BoxedClosure* closure;
FrameInfo* frame_info;
CompiledFunction* cf = impl->getCF();
ScopeInfo* scope_info = cf->clfunc->source->getScopeInfo();
CLFunction* clfunc = impl->getCL();
ScopeInfo* scope_info = clfunc->source->getScopeInfo();
if (scope_info->areLocalsFromModule()) {
// TODO we should cache this in frame_info->locals or something so that locals()
// (and globals() too) will always return the same dict
RELEASE_ASSERT(cf->clfunc->source->scoping->areGlobalsFromModule(), "");
return cf->clfunc->source->parent_module->getAttrWrapper();
RELEASE_ASSERT(clfunc->source->scoping->areGlobalsFromModule(), "");
return clfunc->source->parent_module->getAttrWrapper();
}
if (impl->getId().type == PythonFrameId::COMPILED) {
CompiledFunction* cf = impl->getCF();
d = new BoxedDict();
uint64_t ip = impl->getId().ip;
......@@ -1081,24 +1110,18 @@ Box* PythonFrameIterator::fastLocalsToBoxedLocals() {
return frame_info->boxedLocals;
}
ExecutionPoint getExecutionPoint() {
auto frame = getTopPythonFrame();
auto cf = frame->getCF();
auto current_stmt = frame->getCurrentStatement();
return ExecutionPoint({.cf = cf, .current_stmt = current_stmt });
}
std::unique_ptr<ExecutionPoint> PythonFrameIterator::getExecutionPoint() {
assert(impl.get());
auto cf = impl->getCF();
auto stmt = impl->getCurrentStatement();
return std::unique_ptr<ExecutionPoint>(new ExecutionPoint({.cf = cf, .current_stmt = stmt }));
AST_stmt* PythonFrameIterator::getCurrentStatement() {
return impl->getCurrentStatement();
}
CompiledFunction* PythonFrameIterator::getCF() {
return impl->getCF();
}
CLFunction* PythonFrameIterator::getCL() {
return impl->getCL();
}
Box* PythonFrameIterator::getGlobalsDict() {
return impl->getGlobalsDict();
}
......
......@@ -51,11 +51,7 @@ void unwindingThroughFrame(PythonUnwindSession* unwind_session, unw_cursor_t* cu
void exceptionCaughtInInterpreter(LineInfo line_info, ExcInfo* exc_info);
struct ExecutionPoint {
CompiledFunction* cf;
AST_stmt* current_stmt;
};
ExecutionPoint getExecutionPoint();
CLFunction* getTopPythonFunction();
// debugging/stat helper, returns python filename:linenumber, or "unknown:-1" if it fails
std::string getCurrentPythonLine();
......@@ -73,9 +69,10 @@ private:
public:
CompiledFunction* getCF();
CLFunction* getCL();
FrameInfo* getFrameInfo();
bool exists() { return impl.get() != NULL; }
std::unique_ptr<ExecutionPoint> getExecutionPoint();
AST_stmt* getCurrentStatement();
Box* fastLocalsToBoxedLocals();
Box* getGlobalsDict();
......@@ -114,13 +111,19 @@ struct FrameStackState {
// after the frame ends.
FrameInfo* frame_info;
FrameStackState() {}
FrameStackState(BoxedDict* locals, FrameInfo* frame_info) : locals(locals), frame_info(frame_info) {}
};
// Returns all the stack locals, including hidden ones.
FrameStackState getFrameStackState();
CompiledFunction* getTopCompiledFunction();
struct DeoptState {
FrameStackState frame_state;
CompiledFunction* cf;
AST_stmt* current_stmt;
};
DeoptState getDeoptState();
}
#endif
......@@ -2472,6 +2472,8 @@ void CFG::print() {
}
CFG* computeCFG(SourceInfo* source, std::vector<AST_stmt*> body) {
STAT_TIMER(t0, "us_timer_computecfg", 0);
CFG* rtn = new CFG();
ScopingAnalysis* scoping_analysis = source->scoping;
......
......@@ -64,7 +64,6 @@ public:
using gc::GCVisitor;
enum class EffortLevel {
INTERPRETED = 0,
MODERATE = 2,
MAXIMAL = 3,
};
......@@ -229,7 +228,6 @@ public:
llvm::Function* func; // the llvm IR object
FunctionSpecialization* spec;
const OSREntryDescriptor* entry_descriptor;
bool is_interpreted;
union {
Box* (*call)(Box*, Box*, Box*, Box**);
......@@ -246,13 +244,12 @@ public:
int64_t times_called, times_speculation_failed;
ICInvalidator dependent_callsites;
LocationMap* location_map; // only meaningful if this is a compiled frame
LocationMap* location_map;
std::vector<ICInfo*> ics;
std::vector<std::unique_ptr<JitCodeBlock>> code_blocks;
CompiledFunction(llvm::Function* func, FunctionSpecialization* spec, bool is_interpreted, void* code,
EffortLevel effort, const OSREntryDescriptor* entry_descriptor);
CompiledFunction(llvm::Function* func, FunctionSpecialization* spec, void* code, EffortLevel effort,
const OSREntryDescriptor* entry_descriptor);
ConcreteCompilerType* getReturnType();
......@@ -322,6 +319,11 @@ public:
// Please use codeForFunction() to access this:
BoxedCode* code_obj;
// For use by the interpreter/baseline jit:
int times_interpreted;
std::vector<std::unique_ptr<JitCodeBlock>> code_blocks;
ICInvalidator dependent_interp_callsites;
// Functions can provide an "internal" version, which will get called instead
// of the normal dispatch through the functionlist.
// This can be used to implement functions which know how to rewrite themselves,
......@@ -331,22 +333,9 @@ public:
InternalCallable internal_callable = NULL;
CLFunction(int num_args, int num_defaults, bool takes_varargs, bool takes_kwargs,
std::unique_ptr<SourceInfo> source)
: paramspec(num_args, num_defaults, takes_varargs, takes_kwargs),
source(std::move(source)),
param_names(this->source->ast, this->source->getInternedStrings()),
always_use_version(NULL),
code_obj(NULL) {
assert(num_args >= num_defaults);
}
CLFunction(int num_args, int num_defaults, bool takes_varargs, bool takes_kwargs, const ParamNames& param_names)
: paramspec(num_args, num_defaults, takes_varargs, takes_kwargs),
source(nullptr),
param_names(param_names),
always_use_version(NULL),
code_obj(NULL) {
assert(num_args >= num_defaults);
}
std::unique_ptr<SourceInfo> source);
CLFunction(int num_args, int num_defaults, bool takes_varargs, bool takes_kwargs, const ParamNames& param_names);
~CLFunction();
int numReceivedArgs() { return paramspec.totalReceived(); }
......@@ -354,7 +343,7 @@ public:
assert(compiled);
assert((compiled->spec != NULL) + (compiled->entry_descriptor != NULL) == 1);
assert(compiled->clfunc == NULL);
assert(compiled->is_interpreted == (compiled->code == NULL));
assert(compiled->code);
compiled->clfunc = this;
if (compiled->entry_descriptor == NULL) {
......
......@@ -849,7 +849,7 @@ Box* execfile(Box* _fn) {
// Run directly inside the current module:
AST_Module* ast = caching_parse_file(fn->data());
ASSERT(getExecutionPoint().cf->clfunc->source->scoping->areGlobalsFromModule(), "need to pass custom globals in");
ASSERT(getTopPythonFunction()->source->scoping->areGlobalsFromModule(), "need to pass custom globals in");
compileAndRunModule(ast, getCurrentModule());
return None;
......
......@@ -121,8 +121,8 @@ public:
static Box* lineno(Box* obj, void*) {
auto f = static_cast<BoxedFrame*>(obj);
f->update();
std::unique_ptr<ExecutionPoint> fr = f->it.getExecutionPoint();
return boxInt(fr->current_stmt->lineno);
AST_stmt* stmt = f->it.getCurrentStatement();
return boxInt(stmt->lineno);
}
DEFAULT_CLASS(frame_cls);
......@@ -130,11 +130,11 @@ public:
static Box* boxFrame(PythonFrameIterator it) {
FrameInfo* fi = it.getFrameInfo();
if (fi->frame_obj == NULL) {
auto cf = it.getCF();
auto cl = it.getCL();
Box* globals = it.getGlobalsDict();
BoxedFrame* f = fi->frame_obj = new BoxedFrame(std::move(it));
f->_globals = globals;
f->_code = codeForCLFunction(cf->clfunc);
f->_code = codeForCLFunction(cl);
}
return fi->frame_obj;
......
......@@ -162,20 +162,19 @@ extern "C" Box* deopt(AST_expr* expr, Box* value) {
RELEASE_ASSERT(0, "deopt is currently broken...");
FrameStackState frame_state = getFrameStackState();
auto execution_point = getExecutionPoint();
auto deopt_state = getDeoptState();
// Should we only do this selectively?
execution_point.cf->speculationFailed();
deopt_state.cf->speculationFailed();
// Except of exc.type we skip initializing the exc fields inside the JITed code path (small perf improvement) that's
// why we have todo it now if we didn't set an exception (which sets all fields)
if (frame_state.frame_info->exc.type == NULL) {
frame_state.frame_info->exc.traceback = NULL;
frame_state.frame_info->exc.value = NULL;
if (deopt_state.frame_state.frame_info->exc.type == NULL) {
deopt_state.frame_state.frame_info->exc.traceback = NULL;
deopt_state.frame_state.frame_info->exc.value = NULL;
}
return astInterpretFrom(execution_point.cf, expr, execution_point.current_stmt, value, frame_state);
return astInterpretFrom(deopt_state.cf->clfunc, expr, deopt_state.current_stmt, value, deopt_state.frame_state);
}
extern "C" bool softspace(Box* b, bool newval) {
......@@ -2662,16 +2661,12 @@ extern "C" void dumpEx(void* p, int levels) {
printf("Has %ld function versions\n", cl->versions.size());
for (CompiledFunction* cf : cl->versions) {
if (cf->is_interpreted) {
printf("[interpreted]\n");
} else {
bool got_name;
std::string name = g.func_addr_registry.getFuncNameAtAddress(cf->code, true, &got_name);
if (got_name)
printf("%s\n", name.c_str());
else
printf("%p\n", cf->code);
}
bool got_name;
std::string name = g.func_addr_registry.getFuncNameAtAddress(cf->code, true, &got_name);
if (got_name)
printf("%s\n", name.c_str());
else
printf("%p\n", cf->code);
}
}
......@@ -2930,26 +2925,7 @@ static CompiledFunction* pickVersion(CLFunction* f, int num_output_args, Box* oa
abort();
}
EffortLevel new_effort = initialEffort();
// Only the interpreter currently supports non-module-globals:
if (!f->source->scoping->areGlobalsFromModule())
new_effort = EffortLevel::INTERPRETED;
std::vector<ConcreteCompilerType*> arg_types;
for (int i = 0; i < num_output_args; i++) {
if (new_effort == EffortLevel::INTERPRETED) {
arg_types.push_back(UNKNOWN);
} else {
Box* arg = getArg(i, oarg1, oarg2, oarg3, oargs);
assert(arg); // only builtin functions can pass NULL args
arg_types.push_back(typeFromClass(arg->cls));
}
}
FunctionSpecialization* spec = new FunctionSpecialization(UNKNOWN, arg_types);
// this also pushes the new CompiledVersion to the back of the version list:
return compileFunction(f, spec, new_effort, NULL);
return NULL;
}
static llvm::StringRef getFunctionName(CLFunction* f) {
......@@ -3500,7 +3476,7 @@ static Box* callChosenCF(CompiledFunction* chosen_cf, BoxedClosure* closure, Box
// This function exists for the rewriter: astInterpretFunction takes 9 args, but the rewriter
// only supports calling functions with at most 6 since it can currently only pass arguments
// in registers.
static Box* astInterpretHelper(CompiledFunction* f, int num_args, BoxedClosure* closure, BoxedGenerator* generator,
static Box* astInterpretHelper(CLFunction* f, int num_args, BoxedClosure* closure, BoxedGenerator* generator,
Box* globals, Box** _args) {
Box* arg1 = _args[0];
Box* arg2 = _args[1];
......@@ -3514,16 +3490,15 @@ Box* callCLFunc(CLFunction* f, CallRewriteArgs* rewrite_args, int num_output_arg
BoxedGenerator* generator, Box* globals, Box* oarg1, Box* oarg2, Box* oarg3, Box** oargs) {
CompiledFunction* chosen_cf = pickVersion(f, num_output_args, oarg1, oarg2, oarg3, oargs);
assert(chosen_cf->is_interpreted == (chosen_cf->code == NULL));
if (chosen_cf->is_interpreted) {
if (!chosen_cf) {
if (rewrite_args) {
rewrite_args->rewriter->addDependenceOn(chosen_cf->dependent_callsites);
RewriterVar::SmallVector arg_vec;
rewrite_args->rewriter->addDependenceOn(f->dependent_interp_callsites);
// TODO this kind of embedded reference needs to be tracked by the GC somehow?
// Or maybe it's ok, since we've guarded on the function object?
arg_vec.push_back(rewrite_args->rewriter->loadConst((intptr_t)chosen_cf, Location::forArg(0)));
arg_vec.push_back(rewrite_args->rewriter->loadConst((intptr_t)f, Location::forArg(0)));
arg_vec.push_back(rewrite_args->rewriter->loadConst((intptr_t)num_output_args, Location::forArg(1)));
arg_vec.push_back(rewrite_args->rewriter->loadConst((intptr_t)closure, Location::forArg(2)));
arg_vec.push_back(rewrite_args->rewriter->loadConst((intptr_t)generator, Location::forArg(3)));
......@@ -3531,6 +3506,7 @@ Box* callCLFunc(CLFunction* f, CallRewriteArgs* rewrite_args, int num_output_arg
// Hacky workaround: the rewriter can only pass arguments in registers, so use this helper function
// to unpack some of the additional arguments:
// TODO if there's only one arg we could just pass it normally
RewriterVar* arg_array = rewrite_args->rewriter->allocate(4);
arg_vec.push_back(arg_array);
if (num_output_args >= 1)
......@@ -3546,8 +3522,7 @@ Box* callCLFunc(CLFunction* f, CallRewriteArgs* rewrite_args, int num_output_arg
rewrite_args->out_success = true;
}
return astInterpretFunction(chosen_cf, num_output_args, closure, generator, globals, oarg1, oarg2, oarg3,
oargs);
return astInterpretFunction(f, num_output_args, closure, generator, globals, oarg1, oarg2, oarg3, oargs);
}
ASSERT(!globals, "need to update the calling conventions if we want to pass globals");
......
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