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
......@@ -79,7 +79,7 @@ class ASTInterpreter : public Box {
public:
typedef ContiguousMap<InternedString, Box*> SymMap;
ASTInterpreter(CompiledFunction* compiled_function);
ASTInterpreter(CLFunction* clfunc);
void initArguments(int nargs, BoxedClosure* closure, BoxedGenerator* generator, Box* arg1, Box* arg2, Box* arg3,
Box** args);
......@@ -155,7 +155,7 @@ private:
CFGBlock* next_block, *current_block;
AST_stmt* current_inst;
CompiledFunction* compiled_func;
CLFunction* clfunc;
SourceInfo* source_info;
ScopeInfo* scope_info;
PhiAnalysis* phis;
......@@ -185,7 +185,7 @@ public:
return globals;
}
CompiledFunction* getCF() { return compiled_func; }
CLFunction* getCL() { return clfunc; }
FrameInfo* getFrameInfo() { return &frame_info; }
BoxedClosure* getPassedClosure() { return passed_closure; }
const SymMap& getSymbolTable() { return sym_table; }
......@@ -263,11 +263,11 @@ void ASTInterpreter::gcHandler(GCVisitor* visitor, Box* box) {
interp->frame_info.gcVisit(visitor);
}
ASTInterpreter::ASTInterpreter(CompiledFunction* compiled_function)
ASTInterpreter::ASTInterpreter(CLFunction* clfunc)
: current_block(0),
current_inst(0),
compiled_func(compiled_function),
source_info(compiled_function->clfunc->source.get()),
clfunc(clfunc),
source_info(clfunc->source.get()),
scope_info(0),
phis(NULL),
last_exception(NULL, NULL, NULL),
......@@ -279,10 +279,6 @@ ASTInterpreter::ASTInterpreter(CompiledFunction* compiled_function)
globals(0),
frame_addr(0) {
CLFunction* f = compiled_function->clfunc;
if (!source_info->cfg)
source_info->cfg = computeCFG(f->source.get(), f->source->body);
scope_info = source_info->getScopeInfo();
assert(scope_info);
......@@ -300,7 +296,7 @@ void ASTInterpreter::initArguments(int nargs, BoxedClosure* _closure, BoxedGener
for (int i = 3; i < nargs; ++i)
argsArray.push_back(args[i - 3]);
const ParamNames& param_names = compiled_func->clfunc->param_names;
const ParamNames& param_names = clfunc->param_names;
int i = 0;
for (auto& name : param_names.args) {
......@@ -345,7 +341,7 @@ void ASTInterpreter::startJITing(CFGBlock* block, int exit_offset) {
assert(ENABLE_BASELINEJIT);
assert(!jit);
auto& code_blocks = compiled_func->code_blocks;
auto& code_blocks = clfunc->code_blocks;
JitCodeBlock* code_block = NULL;
if (!code_blocks.empty())
code_block = code_blocks[code_blocks.size() - 1].get();
......@@ -387,7 +383,7 @@ Box* ASTInterpreter::execJITedBlock(CFGBlock* b) {
if (stmt->type != AST_TYPE::Invoke)
throw e;
auto source = getCF()->clfunc->source.get();
auto source = getCL()->source.get();
exceptionCaughtInInterpreter(LineInfo(stmt->lineno, stmt->col_offset, source->fn, source->getName()), &e);
next_block = ((AST_Invoke*)stmt)->exc_dest;
......@@ -412,7 +408,7 @@ Value ASTInterpreter::executeInner(ASTInterpreter& interpreter, CFGBlock* start_
start_block = interpreter.source_info->cfg->getStartingBlock();
start_at = start_block->body[0];
if (ENABLE_BASELINEJIT && interpreter.compiled_func->times_called >= REOPT_THRESHOLD_INTERPRETER
if (ENABLE_BASELINEJIT && interpreter.clfunc->times_interpreted >= REOPT_THRESHOLD_INTERPRETER
&& !start_block->code)
should_jit = true;
}
......@@ -475,6 +471,16 @@ Value ASTInterpreter::executeInner(ASTInterpreter& interpreter, CFGBlock* start_
Value ASTInterpreter::execute(ASTInterpreter& interpreter, CFGBlock* start_block, AST_stmt* start_at) {
UNAVOIDABLE_STAT_TIMER(t0, "us_timer_in_interpreter");
// Note: due to some (avoidable) restrictions, this check is pretty constrained in where
// it can go, due to the fact that it can throw an exception.
// It can't go in the ASTInterpreter constructor, since that will cause the C++ runtime to
// delete the partially-constructed memory which we don't currently handle. It can't go into
// executeInner since we want the SyntaxErrors to happen *before* the stack frame is entered.
// (For instance, throwing the exception will try to fetch the current statement, but we determine
// that by looking at the cfg.)
if (!interpreter.source_info->cfg)
interpreter.source_info->cfg = computeCFG(interpreter.source_info, interpreter.source_info->body);
RegisterHelper frame_registerer;
return executeInner(interpreter, start_block, start_at, &frame_registerer);
}
......@@ -640,7 +646,7 @@ Value ASTInterpreter::visit_branch(AST_Branch* node) {
}
Value ASTInterpreter::visit_jump(AST_Jump* node) {
bool backedge = node->target->idx < current_block->idx && compiled_func;
bool backedge = node->target->idx < current_block->idx;
if (backedge) {
threading::allowGLReadPreemption();
......@@ -681,7 +687,7 @@ Box* ASTInterpreter::doOSR(AST_Jump* node) {
LivenessAnalysis* liveness = source_info->getLiveness();
std::unique_ptr<PhiAnalysis> phis
= computeRequiredPhis(compiled_func->clfunc->param_names, source_info->cfg, liveness, scope_info);
= computeRequiredPhis(clfunc->param_names, source_info->cfg, liveness, scope_info);
std::vector<InternedString> dead_symbols;
for (auto& it : sym_table) {
......@@ -696,9 +702,7 @@ Box* ASTInterpreter::doOSR(AST_Jump* node) {
sym_table.erase(dead);
const OSREntryDescriptor* found_entry = nullptr;
for (auto& p : compiled_func->clfunc->osr_versions) {
if (p.first->cf != compiled_func)
continue;
for (auto& p : clfunc->osr_versions) {
if (p.first->backedge != node)
continue;
......@@ -748,7 +752,7 @@ Box* ASTInterpreter::doOSR(AST_Jump* node) {
sorted_symbol_table[source_info->getInternedStrings().get(FRAME_INFO_PTR_NAME)] = (Box*)&frame_info;
if (found_entry == nullptr) {
OSREntryDescriptor* entry = OSREntryDescriptor::create(compiled_func, node);
OSREntryDescriptor* entry = OSREntryDescriptor::create(clfunc, node);
for (auto& it : sorted_symbol_table) {
if (isIsDefinedName(it.first))
......@@ -768,7 +772,7 @@ Box* ASTInterpreter::doOSR(AST_Jump* node) {
found_entry = entry;
}
OSRExit exit(compiled_func, found_entry);
OSRExit exit(found_entry);
std::vector<Box*, StlCompatAllocator<Box*>> arg_array;
for (auto& it : sorted_symbol_table) {
......@@ -798,7 +802,7 @@ Value ASTInterpreter::visit_invoke(AST_Invoke* node) {
} catch (ExcInfo e) {
abortJITing();
auto source = getCF()->clfunc->source.get();
auto source = getCL()->source.get();
exceptionCaughtInInterpreter(LineInfo(node->lineno, node->col_offset, source->fn, source->getName()), &e);
next_block = node->exc_dest;
......@@ -1029,7 +1033,7 @@ Box* ASTInterpreter::createFunction(AST* node, AST_arguments* args, const std::v
}
Box* passed_globals = NULL;
if (!getCF()->clfunc->source->scoping->areGlobalsFromModule())
if (!getCL()->source->scoping->areGlobalsFromModule())
passed_globals = globals;
return boxCLFunction(cl, closure, passed_globals, u.il);
}
......@@ -1078,7 +1082,7 @@ Value ASTInterpreter::visit_makeClass(AST_MakeClass* mkclass) {
CLFunction* cl = wrapFunction(node, nullptr, node->body, source_info);
Box* passed_globals = NULL;
if (!getCF()->clfunc->source->scoping->areGlobalsFromModule())
if (!getCL()->source->scoping->areGlobalsFromModule())
passed_globals = globals;
Box* attrDict = runtimeCall(boxCLFunction(cl, closure, passed_globals, {}), ArgPassSpec(0), 0, 0, 0, 0, 0);
......@@ -1656,17 +1660,45 @@ void ASTInterpreterJitInterface::setLocalHelper(void* _interpreter, InternedStri
const void* interpreter_instr_addr = (void*)&ASTInterpreter::executeInner;
Box* astInterpretFunction(CompiledFunction* cf, int nargs, Box* closure, Box* generator, Box* globals, Box* arg1,
Box* astInterpretFunction(CLFunction* clfunc, int nargs, Box* closure, Box* generator, Box* globals, Box* arg1,
Box* arg2, Box* arg3, Box** args) {
UNAVOIDABLE_STAT_TIMER(t0, "us_timer_in_interpreter");
assert((!globals) == cf->clfunc->source->scoping->areGlobalsFromModule());
SourceInfo* source_info = clfunc->source.get();
assert((!globals) == source_info->scoping->areGlobalsFromModule());
bool can_reopt = ENABLE_REOPT && !FORCE_INTERPRETER && (globals == NULL);
int num_blocks = cf->clfunc->source->cfg->blocks.size();
// If the cfg hasn't been computed yet, just conservatively say that it will be a big function.
// It shouldn't matter, since the cfg should only be NULL if this is the first execution of this
// function.
int num_blocks = source_info->cfg ? source_info->cfg->blocks.size() : 10000;
int threshold = num_blocks <= 20 ? (REOPT_THRESHOLD_BASELINE / 3) : REOPT_THRESHOLD_BASELINE;
if (unlikely(can_reopt && cf->times_called > threshold)) {
if (unlikely(can_reopt && (FORCE_OPTIMIZE || !ENABLE_INTERPRETER || clfunc->times_interpreted > threshold))) {
assert(!globals);
CompiledFunction* optimized = reoptCompiledFuncInternal(cf);
clfunc->times_interpreted = 0;
EffortLevel new_effort = EffortLevel::MODERATE;
if (FORCE_OPTIMIZE)
new_effort = EffortLevel::MAXIMAL;
std::vector<ConcreteCompilerType*> arg_types;
for (int i = 0; i < nargs; i++) {
Box* arg = getArg(i, arg1, arg2, arg3, args);
assert(arg); // only builtin functions can pass NULL args
// TODO: reenable argument-type specialization
arg_types.push_back(UNKNOWN);
// 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:
CompiledFunction* optimized = compileFunction(clfunc, spec, new_effort, NULL);
clfunc->dependent_interp_callsites.invalidateAll();
if (closure && generator)
return optimized->closure_generator_call((BoxedClosure*)closure, (BoxedGenerator*)generator, arg1, arg2,
arg3, args);
......@@ -1677,16 +1709,15 @@ Box* astInterpretFunction(CompiledFunction* cf, int nargs, Box* closure, Box* ge
return optimized->call(arg1, arg2, arg3, args);
}
++cf->times_called;
ASTInterpreter* interpreter = new ASTInterpreter(cf);
++clfunc->times_interpreted;
ASTInterpreter* interpreter = new ASTInterpreter(clfunc);
ScopeInfo* scope_info = cf->clfunc->source->getScopeInfo();
SourceInfo* source_info = cf->clfunc->source.get();
ScopeInfo* scope_info = clfunc->source->getScopeInfo();
if (unlikely(scope_info->usesNameLookup())) {
interpreter->setBoxedLocals(new BoxedDict());
}
assert((!globals) == cf->clfunc->source->scoping->areGlobalsFromModule());
assert((!globals) == clfunc->source->scoping->areGlobalsFromModule());
if (globals) {
interpreter->setGlobals(globals);
} else {
......@@ -1699,17 +1730,17 @@ Box* astInterpretFunction(CompiledFunction* cf, int nargs, Box* closure, Box* ge
return v.o ? v.o : None;
}
Box* astInterpretFunctionEval(CompiledFunction* cf, Box* globals, Box* boxedLocals) {
++cf->times_called;
Box* astInterpretFunctionEval(CLFunction* clfunc, Box* globals, Box* boxedLocals) {
++clfunc->times_interpreted;
ASTInterpreter* interpreter = new ASTInterpreter(cf);
ASTInterpreter* interpreter = new ASTInterpreter(clfunc);
interpreter->initArguments(0, NULL, NULL, NULL, NULL, NULL, NULL);
interpreter->setBoxedLocals(boxedLocals);
ScopeInfo* scope_info = cf->clfunc->source->getScopeInfo();
SourceInfo* source_info = cf->clfunc->source.get();
ScopeInfo* scope_info = clfunc->source->getScopeInfo();
SourceInfo* source_info = clfunc->source.get();
assert(!cf->clfunc->source->scoping->areGlobalsFromModule());
assert(!clfunc->source->scoping->areGlobalsFromModule());
assert(globals);
interpreter->setGlobals(globals);
......@@ -1718,19 +1749,19 @@ Box* astInterpretFunctionEval(CompiledFunction* cf, Box* globals, Box* boxedLoca
return v.o ? v.o : None;
}
Box* astInterpretFrom(CompiledFunction* cf, AST_expr* after_expr, AST_stmt* enclosing_stmt, Box* expr_val,
Box* astInterpretFrom(CLFunction* clfunc, AST_expr* after_expr, AST_stmt* enclosing_stmt, Box* expr_val,
FrameStackState frame_state) {
assert(cf);
assert(clfunc);
assert(enclosing_stmt);
assert(frame_state.locals);
assert(after_expr);
assert(expr_val);
ASTInterpreter* interpreter = new ASTInterpreter(cf);
ASTInterpreter* interpreter = new ASTInterpreter(clfunc);
ScopeInfo* scope_info = cf->clfunc->source->getScopeInfo();
SourceInfo* source_info = cf->clfunc->source.get();
assert(cf->clfunc->source->scoping->areGlobalsFromModule());
ScopeInfo* scope_info = clfunc->source->getScopeInfo();
SourceInfo* source_info = clfunc->source.get();
assert(clfunc->source->scoping->areGlobalsFromModule());
interpreter->setGlobals(source_info->parent_module);
for (const auto& p : frame_state.locals->d) {
......@@ -1743,7 +1774,7 @@ Box* astInterpretFrom(CompiledFunction* cf, AST_expr* after_expr, AST_stmt* encl
} else if (name == CREATED_CLOSURE_NAME) {
interpreter->setCreatedClosure(p.second);
} else {
InternedString interned = cf->clfunc->source->getInternedStrings().get(name);
InternedString interned = clfunc->source->getInternedStrings().get(name);
interpreter->addSymbol(interned, p.second, false);
}
}
......@@ -1779,7 +1810,7 @@ Box* astInterpretFrom(CompiledFunction* cf, AST_expr* after_expr, AST_stmt* encl
if (start_block == NULL) {
// TODO innefficient
for (auto block : cf->clfunc->source->cfg->blocks) {
for (auto block : clfunc->source->cfg->blocks) {
int n = block->body.size();
for (int i = 0; i < n; i++) {
if (block->body[i] == enclosing_stmt) {
......@@ -1815,10 +1846,10 @@ Box* getGlobalsForInterpretedFrame(void* frame_ptr) {
return interpreter->getGlobals();
}
CompiledFunction* getCFForInterpretedFrame(void* frame_ptr) {
CLFunction* getCLForInterpretedFrame(void* frame_ptr) {
ASTInterpreter* interpreter = s_interpreterMap[frame_ptr];
assert(interpreter);
return interpreter->getCF();
return interpreter->getCL();
}
FrameInfo* getFrameInfoForInterpretedFrame(void* frame_ptr) {
......
......@@ -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; }
};
}
......
......@@ -127,23 +127,14 @@ LivenessAnalysis* SourceInfo::getLiveness() {
return liveness_info.get();
}
EffortLevel initialEffort() {
if (FORCE_INTERPRETER)
return EffortLevel::INTERPRETED;
if (FORCE_OPTIMIZE)
return EffortLevel::MAXIMAL;
if (ENABLE_INTERPRETER)
return EffortLevel::INTERPRETED;
return EffortLevel::MODERATE;
}
static void compileIR(CompiledFunction* cf, EffortLevel effort) {
assert(cf);
assert(cf->func);
void* compiled = NULL;
cf->code = NULL;
if (effort > EffortLevel::INTERPRETED) {
{
Timer _t("to jit the IR");
#if LLVMREV < 215967
g.engine->addModule(cf->func->getParent());
......@@ -195,7 +186,7 @@ CompiledFunction* compileFunction(CLFunction* f, FunctionSpecialization* spec, E
ASSERT(f->versions.size() < 20, "%s %ld", name.c_str(), f->versions.size());
if (VERBOSITY("irgen") >= 2 || (VERBOSITY("irgen") == 1 && effort > EffortLevel::INTERPRETED)) {
if (VERBOSITY("irgen") >= 1) {
std::string s;
llvm::raw_string_ostream ss(s);
......@@ -233,13 +224,6 @@ CompiledFunction* compileFunction(CLFunction* f, FunctionSpecialization* spec, E
printf("%s", ss.str().c_str());
}
#ifndef NDEBUG
if (effort == EffortLevel::INTERPRETED) {
for (auto arg_type : spec->arg_types)
assert(arg_type == UNKNOWN);
}
#endif
// Do the analysis now if we had deferred it earlier:
if (source->cfg == NULL) {
source->cfg = computeCFG(source, source->body);
......@@ -247,17 +231,10 @@ CompiledFunction* compileFunction(CLFunction* f, FunctionSpecialization* spec, E
CompiledFunction* cf = 0;
if (effort == EffortLevel::INTERPRETED) {
assert(!entry_descriptor);
cf = new CompiledFunction(0, spec, true, NULL, effort, 0);
} else {
cf = doCompile(source, &f->param_names, entry_descriptor, effort, spec, name);
compileIR(cf, effort);
}
CompiledFunction* cf = doCompile(f, source, &f->param_names, entry_descriptor, effort, spec, name);
compileIR(cf, effort);
f->addVersion(cf);
assert(f->versions.size());
long us = _t.end();
static StatCounter us_compiling("us_compiling");
......@@ -270,13 +247,6 @@ CompiledFunction* compileFunction(CLFunction* f, FunctionSpecialization* spec, E
num_compiles.log();
switch (effort) {
case EffortLevel::INTERPRETED: {
static StatCounter us_compiling("us_compiling_0_interpreted");
us_compiling.log(us);
static StatCounter num_compiles("num_compiles_0_interpreted");
num_compiles.log();
break;
}
case EffortLevel::MODERATE: {
static StatCounter us_compiling("us_compiling_2_moderate");
us_compiling.log(us);
......@@ -299,7 +269,7 @@ CompiledFunction* compileFunction(CLFunction* f, FunctionSpecialization* spec, E
}
void compileAndRunModule(AST_Module* m, BoxedModule* bm) {
CompiledFunction* cf;
CLFunction* clfunc;
{ // scope for limiting the locked region:
LOCK_REGION(codegen_rwlock.asWrite());
......@@ -317,23 +287,12 @@ void compileAndRunModule(AST_Module* m, BoxedModule* bm) {
if (!bm->hasattr("__builtins__"))
bm->giveAttr("__builtins__", PyModule_GetDict(builtins_module));
CLFunction* cl_f = new CLFunction(0, 0, false, false, std::move(si));
EffortLevel effort = initialEffort();
assert(scoping->areGlobalsFromModule());
cf = compileFunction(cl_f, new FunctionSpecialization(NONE), effort, NULL);
assert(cf->clfunc->versions.size());
clfunc = new CLFunction(0, 0, false, false, std::move(si));
}
if (cf->is_interpreted) {
UNAVOIDABLE_STAT_TIMER(t0, "us_timer_interpreted_module_toplevel");
astInterpretFunction(cf, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
} else {
UNAVOIDABLE_STAT_TIMER(t1, "us_timer_jitted_module_toplevel");
((void (*)())cf->code)();
}
UNAVOIDABLE_STAT_TIMER(t0, "us_timer_interpreted_module_toplevel");
Box* r = astInterpretFunction(clfunc, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
assert(r == None);
}
Box* evalOrExec(CLFunction* cl, Box* globals, Box* boxedLocals) {
......@@ -341,24 +300,13 @@ Box* evalOrExec(CLFunction* cl, Box* globals, Box* boxedLocals) {
assert(globals && (globals->cls == module_cls || globals->cls == dict_cls));
// TODO Right now we only support going into an exec or eval through the
// intepretter, since the interpretter has a special function which lets
// us set the locals object. We should probably support it for optimized
// code as well, so we could use initialEffort() here instead of hard-coding
// INTERPRETED. This could actually be useful if we actually cache the parse
// results (since sometimes eval or exec might be called on constant strings).
EffortLevel effort = EffortLevel::INTERPRETED;
Box* doc_string = cl->source->getDocString();
if (doc_string != None) {
static BoxedString* doc_box = static_cast<BoxedString*>(PyString_InternFromString("__doc__"));
setGlobal(boxedLocals, doc_box, doc_string);
}
CompiledFunction* cf = compileFunction(cl, new FunctionSpecialization(NONE), effort, NULL);
assert(cf->clfunc->versions.size());
return astInterpretFunctionEval(cf, globals, boxedLocals);
return astInterpretFunctionEval(cl, globals, boxedLocals);
}
CLFunction* compileForEvalOrExec(AST* source, std::vector<AST_stmt*> body, std::string fn, PyCompilerFlags* flags) {
......@@ -502,10 +450,10 @@ Box* compile(Box* source, Box* fn, Box* type, Box** _args) {
if (dont_inherit) {
future_flags = arg_future_flags;
} else {
CompiledFunction* caller_cf = getTopCompiledFunction();
assert(caller_cf != NULL);
assert(caller_cf->clfunc->source != NULL);
FutureFlags caller_future_flags = caller_cf->clfunc->source->future_flags;
CLFunction* caller_cl = getTopPythonFunction();
assert(caller_cl != NULL);
assert(caller_cl->source != NULL);
FutureFlags caller_future_flags = caller_cl->source->future_flags;
future_flags = arg_future_flags | caller_future_flags;
}
......@@ -602,10 +550,10 @@ static Box* evalMain(Box* boxedCode, Box* globals, Box* locals, PyCompilerFlags*
}
Box* eval(Box* boxedCode, Box* globals, Box* locals) {
CompiledFunction* caller_cf = getTopCompiledFunction();
assert(caller_cf != NULL);
assert(caller_cf->clfunc->source != NULL);
FutureFlags caller_future_flags = caller_cf->clfunc->source->future_flags;
CLFunction* caller_cl = getTopPythonFunction();
assert(caller_cl != NULL);
assert(caller_cl->source != NULL);
FutureFlags caller_future_flags = caller_cl->source->future_flags;
PyCompilerFlags pcf;
pcf.cf_flags = caller_future_flags;
......@@ -742,13 +690,12 @@ void CompiledFunction::speculationFailed() {
}
}
CompiledFunction::CompiledFunction(llvm::Function* func, FunctionSpecialization* spec, bool is_interpreted, void* code,
EffortLevel effort, const OSREntryDescriptor* entry_descriptor)
CompiledFunction::CompiledFunction(llvm::Function* func, FunctionSpecialization* spec, void* code, EffortLevel effort,
const OSREntryDescriptor* entry_descriptor)
: clfunc(NULL),
func(func),
spec(spec),
entry_descriptor(entry_descriptor),
is_interpreted(is_interpreted),
code(code),
effort(effort),
times_called(0),
......@@ -758,9 +705,11 @@ CompiledFunction::CompiledFunction(llvm::Function* func, FunctionSpecialization*
}
ConcreteCompilerType* CompiledFunction::getReturnType() {
assert(((bool)spec) ^ ((bool)entry_descriptor));
if (spec)
return spec->rtn_type;
return entry_descriptor->cf->getReturnType();
else
return UNKNOWN;
}
/// Reoptimizes the given function version at the new effort level.
......@@ -809,18 +758,16 @@ CompiledFunction* compilePartialFuncInternal(OSRExit* exit) {
LOCK_REGION(codegen_rwlock.asWrite());
assert(exit);
assert(exit->parent_cf);
assert(exit->parent_cf->effort < EffortLevel::MAXIMAL);
stat_osrexits.log();
// if (VERBOSITY("irgen") >= 1) printf("In compilePartialFunc, handling %p\n", exit);
assert(exit->parent_cf->clfunc);
CompiledFunction*& new_cf = exit->parent_cf->clfunc->osr_versions[exit->entry];
CLFunction* clfunc = exit->entry->clfunc;
assert(clfunc);
CompiledFunction*& new_cf = clfunc->osr_versions[exit->entry];
if (new_cf == NULL) {
EffortLevel new_effort = exit->parent_cf->effort == EffortLevel::INTERPRETED ? EffortLevel::MODERATE
: EffortLevel::MAXIMAL;
CompiledFunction* compiled = compileFunction(exit->parent_cf->clfunc, NULL, new_effort, exit->entry);
EffortLevel new_effort = EffortLevel::MAXIMAL;
CompiledFunction* compiled = compileFunction(clfunc, NULL, new_effort, exit->entry);
assert(compiled == new_cf);
stat_osr_compiles.log();
......@@ -843,16 +790,9 @@ extern "C" CompiledFunction* reoptCompiledFuncInternal(CompiledFunction* cf) {
assert(cf->effort < EffortLevel::MAXIMAL);
assert(cf->clfunc->versions.size());
EffortLevel new_effort;
if (cf->effort == EffortLevel::INTERPRETED)
new_effort = EffortLevel::MODERATE;
else if (cf->effort == EffortLevel::MODERATE)
new_effort = EffortLevel::MAXIMAL;
else
RELEASE_ASSERT(0, "unknown effort: %d", cf->effort);
EffortLevel new_effort = EffortLevel::MAXIMAL;
CompiledFunction* new_cf = _doReopt(cf, new_effort);
assert(!new_cf->is_interpreted);
return new_cf;
}
......@@ -905,6 +845,6 @@ void addRTFunction(CLFunction* cl_f, void* f, ConcreteCompilerType* rtn_type,
#endif
FunctionSpecialization* spec = new FunctionSpecialization(processType(rtn_type), arg_types);
cl_f->addVersion(new CompiledFunction(NULL, spec, false, f, EffortLevel::MAXIMAL, NULL));
cl_f->addVersion(new CompiledFunction(NULL, spec, f, EffortLevel::MAXIMAL, NULL));
}
}
......@@ -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