Commit 7cf92757 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Cleanup: add settable OSR/reopt thresholds, and get rid of tier 2

Previously it was:
tier 0: ast interpreter
tier 1: llvm, no speculations, no llvm opts
tier 2: llvm, w/ speculations, no llvm opts
tier 3: llvm, w/ speculations, w/ llvm opts

tier 2 seemed pretty useless, and very little would stay in it.  Also,
OSR would always skip from tier 1 to tier 3.

Separately, add configurable OSR/reopt thresholds.  This is mostly for the
sake of tests, where we can set lower limits and force OSR/reopts to happen.
parent b8a00bf1
...@@ -783,8 +783,7 @@ public: ...@@ -783,8 +783,7 @@ public:
// public entry point: // public entry point:
TypeAnalysis* doTypeAnalysis(CFG* cfg, const ParamNames& arg_names, const std::vector<ConcreteCompilerType*>& arg_types, TypeAnalysis* doTypeAnalysis(CFG* cfg, const ParamNames& arg_names, const std::vector<ConcreteCompilerType*>& arg_types,
EffortLevel::EffortLevel effort, TypeAnalysis::SpeculationLevel speculation, EffortLevel effort, TypeAnalysis::SpeculationLevel speculation, ScopeInfo* scope_info) {
ScopeInfo* scope_info) {
// if (effort == EffortLevel::INTERPRETED) { // if (effort == EffortLevel::INTERPRETED) {
// return new NullTypeAnalysis(); // return new NullTypeAnalysis();
//} //}
......
...@@ -43,7 +43,7 @@ public: ...@@ -43,7 +43,7 @@ public:
}; };
TypeAnalysis* doTypeAnalysis(CFG* cfg, const ParamNames& param_names, TypeAnalysis* doTypeAnalysis(CFG* cfg, const ParamNames& param_names,
const std::vector<ConcreteCompilerType*>& arg_types, EffortLevel::EffortLevel effort, const std::vector<ConcreteCompilerType*>& arg_types, EffortLevel effort,
TypeAnalysis::SpeculationLevel speculation, ScopeInfo* scope_info); TypeAnalysis::SpeculationLevel speculation, ScopeInfo* scope_info);
} }
......
...@@ -45,9 +45,6 @@ namespace pyston { ...@@ -45,9 +45,6 @@ namespace pyston {
namespace { namespace {
#define OSR_THRESHOLD 200
#define REOPT_THRESHOLD 100
union Value { union Value {
bool b; bool b;
int64_t n; int64_t n;
...@@ -379,7 +376,7 @@ Value ASTInterpreter::visit_jump(AST_Jump* node) { ...@@ -379,7 +376,7 @@ Value ASTInterpreter::visit_jump(AST_Jump* node) {
if (ENABLE_OSR && backedge) { if (ENABLE_OSR && backedge) {
++edgecount; ++edgecount;
if (edgecount > OSR_THRESHOLD && !FORCE_INTERPRETER) { if (edgecount > OSR_THRESHOLD_INTERPRETER && !FORCE_INTERPRETER) {
eraseDeadSymbols(); eraseDeadSymbols();
const OSREntryDescriptor* found_entry = nullptr; const OSREntryDescriptor* found_entry = nullptr;
...@@ -1083,7 +1080,7 @@ const void* interpreter_instr_addr = (void*)&ASTInterpreter::execute; ...@@ -1083,7 +1080,7 @@ const void* interpreter_instr_addr = (void*)&ASTInterpreter::execute;
Box* astInterpretFunction(CompiledFunction* cf, int nargs, Box* closure, Box* generator, Box* arg1, Box* arg2, Box* astInterpretFunction(CompiledFunction* cf, int nargs, Box* closure, Box* generator, Box* arg1, Box* arg2,
Box* arg3, Box** args) { Box* arg3, Box** args) {
if (unlikely(cf->times_called > REOPT_THRESHOLD && ENABLE_REOPT && !FORCE_INTERPRETER)) { if (unlikely(cf->times_called > REOPT_THRESHOLD_INTERPRETER && ENABLE_REOPT && !FORCE_INTERPRETER)) {
CompiledFunction* optimized = reoptCompiledFuncInternal(cf); CompiledFunction* optimized = reoptCompiledFuncInternal(cf);
if (closure && generator) if (closure && generator)
return optimized->closure_generator_call((BoxedClosure*)closure, (BoxedGenerator*)generator, arg1, arg2, return optimized->closure_generator_call((BoxedClosure*)closure, (BoxedGenerator*)generator, arg1, arg2,
......
...@@ -63,7 +63,7 @@ void MyInserter::InsertHelper(llvm::Instruction* I, const llvm::Twine& Name, llv ...@@ -63,7 +63,7 @@ void MyInserter::InsertHelper(llvm::Instruction* I, const llvm::Twine& Name, llv
llvm::IRBuilderDefaultInserter<true>::InsertHelper(I, Name, BB, InsertPt); llvm::IRBuilderDefaultInserter<true>::InsertHelper(I, Name, BB, InsertPt);
} }
static void optimizeIR(llvm::Function* f, EffortLevel::EffortLevel effort) { static void optimizeIR(llvm::Function* f, EffortLevel effort) {
// TODO maybe should do some simple passes (ex: gvn?) if effort level isn't maximal? // TODO maybe should do some simple passes (ex: gvn?) if effort level isn't maximal?
// In general, this function needs a lot of tuning. // In general, this function needs a lot of tuning.
if (effort < EffortLevel::MAXIMAL) if (effort < EffortLevel::MAXIMAL)
...@@ -334,7 +334,7 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua ...@@ -334,7 +334,7 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua
TypeAnalysis* types, const OSREntryDescriptor* entry_descriptor, const BlockSet& full_blocks, TypeAnalysis* types, const OSREntryDescriptor* entry_descriptor, const BlockSet& full_blocks,
const BlockSet& partial_blocks) { const BlockSet& partial_blocks) {
SourceInfo* source = irstate->getSourceInfo(); SourceInfo* source = irstate->getSourceInfo();
EffortLevel::EffortLevel effort = irstate->getEffortLevel(); EffortLevel effort = irstate->getEffortLevel();
CompiledFunction* cf = irstate->getCurFunction(); CompiledFunction* cf = irstate->getCurFunction();
ConcreteCompilerType* rtn_type = irstate->getReturnType(); ConcreteCompilerType* rtn_type = irstate->getReturnType();
// llvm::MDNode* func_info = irstate->getFuncDbgInfo(); // llvm::MDNode* func_info = irstate->getFuncDbgInfo();
...@@ -614,13 +614,6 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua ...@@ -614,13 +614,6 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua
// pass // pass
} else if (block == source->cfg->getStartingBlock()) { } else if (block == source->cfg->getStartingBlock()) {
assert(entry_descriptor == NULL); assert(entry_descriptor == NULL);
// number of times a function needs to be called to be reoptimized:
static const int REOPT_THRESHOLDS[] = {
10, // INTERPRETED->MINIMAL
250, // MINIMAL->MODERATE
10000, // MODERATE->MAXIMAL
};
assert(strcmp("opt", bb_type) == 0); assert(strcmp("opt", bb_type) == 0);
if (ENABLE_REOPT && effort < EffortLevel::MAXIMAL && source->ast != NULL if (ENABLE_REOPT && effort < EffortLevel::MAXIMAL && source->ast != NULL
...@@ -636,8 +629,11 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua ...@@ -636,8 +629,11 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua
llvm::Value* new_call_count llvm::Value* new_call_count
= emitter->getBuilder()->CreateAdd(cur_call_count, getConstantInt(1, g.i64)); = emitter->getBuilder()->CreateAdd(cur_call_count, getConstantInt(1, g.i64));
emitter->getBuilder()->CreateStore(new_call_count, call_count_ptr); emitter->getBuilder()->CreateStore(new_call_count, call_count_ptr);
llvm::Value* reopt_test = emitter->getBuilder()->CreateICmpSGT(
new_call_count, getConstantInt(REOPT_THRESHOLDS[effort], g.i64)); assert(effort == EffortLevel::MINIMAL);
int reopt_threshold = REOPT_THRESHOLD_BASELINE;
llvm::Value* reopt_test
= emitter->getBuilder()->CreateICmpSGT(new_call_count, getConstantInt(reopt_threshold, g.i64));
llvm::Metadata* md_vals[] = { llvm::MDString::get(g.context, "branch_weights"), llvm::Metadata* md_vals[] = { llvm::MDString::get(g.context, "branch_weights"),
llvm::ConstantAsMetadata::get(getConstantInt(1)), llvm::ConstantAsMetadata::get(getConstantInt(1)),
...@@ -1085,13 +1081,12 @@ static llvm::MDNode* setupDebugInfo(SourceInfo* source, llvm::Function* f, std:: ...@@ -1085,13 +1081,12 @@ static llvm::MDNode* setupDebugInfo(SourceInfo* source, llvm::Function* f, std::
return func_info; return func_info;
} }
static std::string getUniqueFunctionName(std::string nameprefix, EffortLevel::EffortLevel effort, static std::string getUniqueFunctionName(std::string nameprefix, EffortLevel effort, const OSREntryDescriptor* entry) {
const OSREntryDescriptor* entry) {
static int num_functions = 0; static int num_functions = 0;
std::ostringstream os; std::ostringstream os;
os << nameprefix; os << nameprefix;
os << "_e" << effort; os << "_e" << (int)effort;
if (entry) { if (entry) {
os << "_osr" << entry->backedge->target->idx; os << "_osr" << entry->backedge->target->idx;
if (entry->cf->func) if (entry->cf->func)
...@@ -1103,7 +1098,7 @@ static std::string getUniqueFunctionName(std::string nameprefix, EffortLevel::Ef ...@@ -1103,7 +1098,7 @@ static std::string getUniqueFunctionName(std::string nameprefix, EffortLevel::Ef
} }
CompiledFunction* doCompile(SourceInfo* source, ParamNames* param_names, const OSREntryDescriptor* entry_descriptor, CompiledFunction* doCompile(SourceInfo* source, ParamNames* param_names, const OSREntryDescriptor* entry_descriptor,
EffortLevel::EffortLevel effort, FunctionSpecialization* spec, std::string nameprefix) { EffortLevel effort, FunctionSpecialization* spec, std::string nameprefix) {
Timer _t("in doCompile"); Timer _t("in doCompile");
Timer _t2; Timer _t2;
long irgen_us = 0; long irgen_us = 0;
...@@ -1170,7 +1165,8 @@ CompiledFunction* doCompile(SourceInfo* source, ParamNames* param_names, const O ...@@ -1170,7 +1165,8 @@ CompiledFunction* doCompile(SourceInfo* source, ParamNames* param_names, const O
irgen_us += _t2.split(); irgen_us += _t2.split();
TypeAnalysis::SpeculationLevel speculation_level = TypeAnalysis::NONE; TypeAnalysis::SpeculationLevel speculation_level = TypeAnalysis::NONE;
if (ENABLE_SPECULATION && effort >= EffortLevel::MODERATE) EffortLevel min_speculation_level = EffortLevel::MAXIMAL;
if (ENABLE_SPECULATION && effort >= min_speculation_level)
speculation_level = TypeAnalysis::SOME; speculation_level = TypeAnalysis::SOME;
TypeAnalysis* types TypeAnalysis* types
= doTypeAnalysis(source->cfg, *param_names, spec->arg_types, effort, speculation_level, source->getScopeInfo()); = doTypeAnalysis(source->cfg, *param_names, spec->arg_types, effort, speculation_level, source->getScopeInfo());
......
...@@ -95,7 +95,7 @@ InternedString getIsDefinedName(InternedString name, InternedStringPool& interne ...@@ -95,7 +95,7 @@ InternedString getIsDefinedName(InternedString name, InternedStringPool& interne
bool isIsDefinedName(const std::string& name); bool isIsDefinedName(const std::string& name);
CompiledFunction* doCompile(SourceInfo* source, ParamNames* param_names, const OSREntryDescriptor* entry_descriptor, CompiledFunction* doCompile(SourceInfo* source, ParamNames* param_names, const OSREntryDescriptor* entry_descriptor,
EffortLevel::EffortLevel effort, FunctionSpecialization* spec, std::string nameprefix); 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 // 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 // potentially-undefined. If it is potentially-undefined, we have to generate control-flow
...@@ -119,13 +119,13 @@ llvm::Value* handlePotentiallyUndefined(ConcreteCompilerVariable* is_defined_var ...@@ -119,13 +119,13 @@ llvm::Value* handlePotentiallyUndefined(ConcreteCompilerVariable* is_defined_var
class TypeRecorder; class TypeRecorder;
class OpInfo { class OpInfo {
private: private:
const EffortLevel::EffortLevel effort; const EffortLevel effort;
TypeRecorder* const type_recorder; TypeRecorder* const type_recorder;
public: public:
const UnwindInfo unw_info; const UnwindInfo unw_info;
OpInfo(EffortLevel::EffortLevel effort, TypeRecorder* type_recorder, UnwindInfo unw_info) OpInfo(EffortLevel effort, TypeRecorder* type_recorder, UnwindInfo unw_info)
: effort(effort), type_recorder(type_recorder), unw_info(unw_info) {} : effort(effort), type_recorder(type_recorder), unw_info(unw_info) {}
bool isInterpreted() const { return effort == EffortLevel::INTERPRETED; } bool isInterpreted() const { return effort == EffortLevel::INTERPRETED; }
......
...@@ -103,7 +103,7 @@ ScopeInfo* SourceInfo::getScopeInfo() { ...@@ -103,7 +103,7 @@ ScopeInfo* SourceInfo::getScopeInfo() {
return scoping->getScopeInfoForNode(ast); return scoping->getScopeInfoForNode(ast);
} }
EffortLevel::EffortLevel initialEffort() { EffortLevel initialEffort() {
if (FORCE_INTERPRETER) if (FORCE_INTERPRETER)
return EffortLevel::INTERPRETED; return EffortLevel::INTERPRETED;
if (FORCE_OPTIMIZE) if (FORCE_OPTIMIZE)
...@@ -113,7 +113,7 @@ EffortLevel::EffortLevel initialEffort() { ...@@ -113,7 +113,7 @@ EffortLevel::EffortLevel initialEffort() {
return EffortLevel::MINIMAL; return EffortLevel::MINIMAL;
} }
static void compileIR(CompiledFunction* cf, EffortLevel::EffortLevel effort) { static void compileIR(CompiledFunction* cf, EffortLevel effort) {
assert(cf); assert(cf);
assert(cf->func); assert(cf->func);
...@@ -165,7 +165,7 @@ static void compileIR(CompiledFunction* cf, EffortLevel::EffortLevel effort) { ...@@ -165,7 +165,7 @@ static void compileIR(CompiledFunction* cf, EffortLevel::EffortLevel effort) {
// Compiles a new version of the function with the given signature and adds it to the list; // Compiles a new version of the function with the given signature and adds it to the list;
// should only be called after checking to see if the other versions would work. // should only be called after checking to see if the other versions would work.
// The codegen_lock needs to be held in W mode before calling this function: // The codegen_lock needs to be held in W mode before calling this function:
CompiledFunction* compileFunction(CLFunction* f, FunctionSpecialization* spec, EffortLevel::EffortLevel effort, CompiledFunction* compileFunction(CLFunction* f, FunctionSpecialization* spec, EffortLevel effort,
const OSREntryDescriptor* entry) { const OSREntryDescriptor* entry) {
Timer _t("for compileFunction()"); Timer _t("for compileFunction()");
assert(spec); assert(spec);
...@@ -190,7 +190,7 @@ CompiledFunction* compileFunction(CLFunction* f, FunctionSpecialization* spec, E ...@@ -190,7 +190,7 @@ CompiledFunction* compileFunction(CLFunction* f, FunctionSpecialization* spec, E
ss << ") -> "; ss << ") -> ";
ss << spec->rtn_type->debugName(); ss << spec->rtn_type->debugName();
// spec->rtn_type->llvmType()->print(ss); // spec->rtn_type->llvmType()->print(ss);
ss << " at effort level " << effort; ss << " at effort level " << (int)effort;
if (entry != NULL) { if (entry != NULL) {
ss << "\nDoing OSR-entry partial compile, starting with backedge to block " << entry->backedge->target->idx ss << "\nDoing OSR-entry partial compile, starting with backedge to block " << entry->backedge->target->idx
<< '\n'; << '\n';
...@@ -250,13 +250,6 @@ CompiledFunction* compileFunction(CLFunction* f, FunctionSpecialization* spec, E ...@@ -250,13 +250,6 @@ CompiledFunction* compileFunction(CLFunction* f, FunctionSpecialization* spec, E
num_compiles.log(); num_compiles.log();
break; break;
} }
case EffortLevel::MODERATE: {
static StatCounter us_compiling("us_compiling_2_moderate");
us_compiling.log(us);
static StatCounter num_compiles("num_compiles_2_moderate");
num_compiles.log();
break;
}
case EffortLevel::MAXIMAL: { case EffortLevel::MAXIMAL: {
static StatCounter us_compiling("us_compiling_3_maximal"); static StatCounter us_compiling("us_compiling_3_maximal");
us_compiling.log(us); us_compiling.log(us);
...@@ -264,6 +257,8 @@ CompiledFunction* compileFunction(CLFunction* f, FunctionSpecialization* spec, E ...@@ -264,6 +257,8 @@ CompiledFunction* compileFunction(CLFunction* f, FunctionSpecialization* spec, E
num_compiles.log(); num_compiles.log();
break; break;
} }
default:
RELEASE_ASSERT(0, "%d", effort);
} }
return cf; return cf;
...@@ -284,7 +279,7 @@ void compileAndRunModule(AST_Module* m, BoxedModule* bm) { ...@@ -284,7 +279,7 @@ void compileAndRunModule(AST_Module* m, BoxedModule* bm) {
SourceInfo* si = new SourceInfo(bm, scoping, m, m->body); SourceInfo* si = new SourceInfo(bm, scoping, m, m->body);
CLFunction* cl_f = new CLFunction(0, 0, false, false, si); CLFunction* cl_f = new CLFunction(0, 0, false, false, si);
EffortLevel::EffortLevel effort = initialEffort(); EffortLevel effort = initialEffort();
cf = compileFunction(cl_f, new FunctionSpecialization(VOID), effort, NULL); cf = compileFunction(cl_f, new FunctionSpecialization(VOID), effort, NULL);
assert(cf->clfunc->versions.size()); assert(cf->clfunc->versions.size());
...@@ -335,7 +330,7 @@ void CompiledFunction::speculationFailed() { ...@@ -335,7 +330,7 @@ void CompiledFunction::speculationFailed() {
/// Reoptimizes the given function version at the new effort level. /// Reoptimizes the given function version at the new effort level.
/// The cf must be an active version in its parents CLFunction; the given /// The cf must be an active version in its parents CLFunction; the given
/// version will be replaced by the new version, which will be returned. /// version will be replaced by the new version, which will be returned.
static CompiledFunction* _doReopt(CompiledFunction* cf, EffortLevel::EffortLevel new_effort) { static CompiledFunction* _doReopt(CompiledFunction* cf, EffortLevel new_effort) {
LOCK_REGION(codegen_rwlock.asWrite()); LOCK_REGION(codegen_rwlock.asWrite());
assert(cf->clfunc->versions.size()); assert(cf->clfunc->versions.size());
...@@ -372,7 +367,8 @@ static CompiledFunction* _doReopt(CompiledFunction* cf, EffortLevel::EffortLevel ...@@ -372,7 +367,8 @@ static CompiledFunction* _doReopt(CompiledFunction* cf, EffortLevel::EffortLevel
abort(); abort();
} }
static StatCounter stat_osrexits("OSR exits"); static StatCounter stat_osrexits("num_osr_exits");
static StatCounter stat_osr_compiles("num_osr_compiles");
CompiledFunction* compilePartialFuncInternal(OSRExit* exit) { CompiledFunction* compilePartialFuncInternal(OSRExit* exit) {
LOCK_REGION(codegen_rwlock.asWrite()); LOCK_REGION(codegen_rwlock.asWrite());
...@@ -386,14 +382,14 @@ CompiledFunction* compilePartialFuncInternal(OSRExit* exit) { ...@@ -386,14 +382,14 @@ CompiledFunction* compilePartialFuncInternal(OSRExit* exit) {
assert(exit->parent_cf->clfunc); assert(exit->parent_cf->clfunc);
CompiledFunction*& new_cf = exit->parent_cf->clfunc->osr_versions[exit->entry]; CompiledFunction*& new_cf = exit->parent_cf->clfunc->osr_versions[exit->entry];
if (new_cf == NULL) { if (new_cf == NULL) {
EffortLevel::EffortLevel new_effort = EffortLevel::MAXIMAL; EffortLevel new_effort = EffortLevel::MAXIMAL;
if (exit->parent_cf->effort == EffortLevel::INTERPRETED) if (exit->parent_cf->effort == EffortLevel::INTERPRETED)
new_effort = EffortLevel::MINIMAL; new_effort = EffortLevel::MINIMAL;
// EffortLevel::EffortLevel new_effort = (EffortLevel::EffortLevel)(exit->parent_cf->effort + 1);
// new_effort = EffortLevel::MAXIMAL;
CompiledFunction* compiled CompiledFunction* compiled
= compileFunction(exit->parent_cf->clfunc, exit->parent_cf->spec, new_effort, exit->entry); = compileFunction(exit->parent_cf->clfunc, exit->parent_cf->spec, new_effort, exit->entry);
assert(compiled == new_cf); assert(compiled == new_cf);
stat_osr_compiles.log();
} }
return new_cf; return new_cf;
...@@ -412,7 +408,16 @@ extern "C" CompiledFunction* reoptCompiledFuncInternal(CompiledFunction* cf) { ...@@ -412,7 +408,16 @@ extern "C" CompiledFunction* reoptCompiledFuncInternal(CompiledFunction* cf) {
assert(cf->effort < EffortLevel::MAXIMAL); assert(cf->effort < EffortLevel::MAXIMAL);
assert(cf->clfunc->versions.size()); assert(cf->clfunc->versions.size());
CompiledFunction* new_cf = _doReopt(cf, (EffortLevel::EffortLevel(cf->effort + 1)));
EffortLevel new_effort;
if (cf->effort == EffortLevel::INTERPRETED)
new_effort = EffortLevel::MINIMAL;
else if (cf->effort == EffortLevel::MINIMAL)
new_effort = EffortLevel::MAXIMAL;
else
RELEASE_ASSERT(0, "unknown effort: %d", cf->effort);
CompiledFunction* new_cf = _doReopt(cf, new_effort);
assert(!new_cf->is_interpreted); assert(!new_cf->is_interpreted);
return new_cf; return new_cf;
} }
......
...@@ -332,7 +332,7 @@ private: ...@@ -332,7 +332,7 @@ private:
OpInfo getOpInfoForNode(AST* ast, UnwindInfo unw_info) { OpInfo getOpInfoForNode(AST* ast, UnwindInfo unw_info) {
assert(ast); assert(ast);
EffortLevel::EffortLevel effort = irstate->getEffortLevel(); EffortLevel effort = irstate->getEffortLevel();
bool record_types = (effort != EffortLevel::INTERPRETED && effort != EffortLevel::MAXIMAL); bool record_types = (effort != EffortLevel::INTERPRETED && effort != EffortLevel::MAXIMAL);
TypeRecorder* type_recorder; TypeRecorder* type_recorder;
...@@ -1945,10 +1945,8 @@ private: ...@@ -1945,10 +1945,8 @@ private:
llvm::Value* newcount = emitter.getBuilder()->CreateAdd(curcount, getConstantInt(1, g.i64)); llvm::Value* newcount = emitter.getBuilder()->CreateAdd(curcount, getConstantInt(1, g.i64));
emitter.getBuilder()->CreateStore(newcount, edgecount_ptr); emitter.getBuilder()->CreateStore(newcount, edgecount_ptr);
int OSR_THRESHOLD = 10000; assert(irstate->getEffortLevel() == EffortLevel::MINIMAL);
if (irstate->getEffortLevel() == EffortLevel::INTERPRETED) llvm::Value* osr_test = emitter.getBuilder()->CreateICmpSGT(newcount, getConstantInt(OSR_THRESHOLD_BASELINE));
OSR_THRESHOLD = 100;
llvm::Value* osr_test = emitter.getBuilder()->CreateICmpSGT(newcount, getConstantInt(OSR_THRESHOLD));
llvm::Metadata* md_vals[] llvm::Metadata* md_vals[]
= { llvm::MDString::get(g.context, "branch_weights"), llvm::ConstantAsMetadata::get(getConstantInt(1)), = { llvm::MDString::get(g.context, "branch_weights"), llvm::ConstantAsMetadata::get(getConstantInt(1)),
......
...@@ -76,7 +76,7 @@ public: ...@@ -76,7 +76,7 @@ public:
llvm::Function* getLLVMFunction() { return cf->func; } llvm::Function* getLLVMFunction() { return cf->func; }
EffortLevel::EffortLevel getEffortLevel() { return cf->effort; } EffortLevel getEffortLevel() { return cf->effort; }
GCBuilder* getGC() { return gc; } GCBuilder* getGC() { return gc; }
......
...@@ -56,7 +56,7 @@ BoxedClass* TypeRecorder::predict() { ...@@ -56,7 +56,7 @@ BoxedClass* TypeRecorder::predict() {
if (!ENABLE_TYPE_FEEDBACK) if (!ENABLE_TYPE_FEEDBACK)
return NULL; return NULL;
if (last_count > 100) if (last_count > SPECULATION_THRESHOLD)
return last_seen; return last_seen;
return NULL; return NULL;
......
...@@ -40,6 +40,12 @@ bool ENABLE_INTERPRETER = true; ...@@ -40,6 +40,12 @@ bool ENABLE_INTERPRETER = true;
bool ENABLE_PYPA_PARSER = false; bool ENABLE_PYPA_PARSER = false;
bool USE_REGALLOC_BASIC = true; bool USE_REGALLOC_BASIC = true;
int OSR_THRESHOLD_INTERPRETER = 200;
int REOPT_THRESHOLD_INTERPRETER = 100;
int OSR_THRESHOLD_BASELINE = 10000;
int REOPT_THRESHOLD_BASELINE = 500;
int SPECULATION_THRESHOLD = 100;
static bool _GLOBAL_ENABLE = 1; static bool _GLOBAL_ENABLE = 1;
bool ENABLE_ICS = 1 && _GLOBAL_ENABLE; bool ENABLE_ICS = 1 && _GLOBAL_ENABLE;
bool ENABLE_ICGENERICS = 1 && ENABLE_ICS; bool ENABLE_ICGENERICS = 1 && ENABLE_ICS;
......
...@@ -31,6 +31,10 @@ inline int version_hex(int major, int minor, int micro, int level = 0, int seria ...@@ -31,6 +31,10 @@ inline int version_hex(int major, int minor, int micro, int level = 0, int seria
extern int MAX_OPT_ITERATIONS; extern int MAX_OPT_ITERATIONS;
extern int OSR_THRESHOLD_INTERPRETER, REOPT_THRESHOLD_INTERPRETER;
extern int OSR_THRESHOLD_BASELINE, REOPT_THRESHOLD_BASELINE;
extern int SPECULATION_THRESHOLD;
extern bool SHOW_DISASM, FORCE_INTERPRETER, FORCE_OPTIMIZE, PROFILE, DUMPJIT, TRAP, USE_STRIPPED_STDLIB, extern bool SHOW_DISASM, FORCE_INTERPRETER, FORCE_OPTIMIZE, PROFILE, DUMPJIT, TRAP, USE_STRIPPED_STDLIB,
ENABLE_INTERPRETER, ENABLE_PYPA_PARSER, USE_REGALLOC_BASIC; ENABLE_INTERPRETER, ENABLE_PYPA_PARSER, USE_REGALLOC_BASIC;
......
...@@ -96,14 +96,11 @@ public: ...@@ -96,14 +96,11 @@ public:
} // namespace gc } // namespace gc
using gc::GCVisitor; using gc::GCVisitor;
namespace EffortLevel { enum class EffortLevel {
enum EffortLevel {
INTERPRETED = 0, INTERPRETED = 0,
MINIMAL, MINIMAL = 1,
MODERATE, MAXIMAL = 3, // keep the old tier numbering for familiarity
MAXIMAL,
}; };
}
class CompilerType; class CompilerType;
template <class V> class ValuedCompilerType; template <class V> class ValuedCompilerType;
...@@ -193,7 +190,7 @@ public: ...@@ -193,7 +190,7 @@ public:
int code_size; int code_size;
llvm::Value* llvm_code; // the llvm callable. llvm::Value* llvm_code; // the llvm callable.
EffortLevel::EffortLevel effort; EffortLevel effort;
int64_t times_called, times_speculation_failed; int64_t times_called, times_speculation_failed;
ICInvalidator dependent_callsites; ICInvalidator dependent_callsites;
...@@ -203,8 +200,7 @@ public: ...@@ -203,8 +200,7 @@ public:
std::vector<ICInfo*> ics; std::vector<ICInfo*> ics;
CompiledFunction(llvm::Function* func, FunctionSpecialization* spec, bool is_interpreted, void* code, CompiledFunction(llvm::Function* func, FunctionSpecialization* spec, bool is_interpreted, void* code,
llvm::Value* llvm_code, EffortLevel::EffortLevel effort, llvm::Value* llvm_code, EffortLevel effort, const OSREntryDescriptor* entry_descriptor)
const OSREntryDescriptor* entry_descriptor)
: clfunc(NULL), func(func), spec(spec), entry_descriptor(entry_descriptor), is_interpreted(is_interpreted), : clfunc(NULL), func(func), spec(spec), entry_descriptor(entry_descriptor), is_interpreted(is_interpreted),
code(code), llvm_code(llvm_code), effort(effort), times_called(0), times_speculation_failed(0), code(code), llvm_code(llvm_code), effort(effort), times_called(0), times_speculation_failed(0),
location_map(nullptr) {} location_map(nullptr) {}
...@@ -327,9 +323,9 @@ CLFunction* unboxRTFunction(Box*); ...@@ -327,9 +323,9 @@ CLFunction* unboxRTFunction(Box*);
// Compiles a new version of the function with the given signature and adds it to the list; // Compiles a new version of the function with the given signature and adds it to the list;
// should only be called after checking to see if the other versions would work. // should only be called after checking to see if the other versions would work.
CompiledFunction* compileFunction(CLFunction* f, FunctionSpecialization* spec, EffortLevel::EffortLevel effort, CompiledFunction* compileFunction(CLFunction* f, FunctionSpecialization* spec, EffortLevel effort,
const OSREntryDescriptor* entry); const OSREntryDescriptor* entry);
EffortLevel::EffortLevel initialEffort(); EffortLevel initialEffort();
typedef bool i1; typedef bool i1;
typedef int64_t i64; typedef int64_t i64;
......
...@@ -27,18 +27,24 @@ static Box* setOption(Box* option, Box* value) { ...@@ -27,18 +27,24 @@ static Box* setOption(Box* option, Box* value) {
if (value->cls != int_cls) if (value->cls != int_cls)
raiseExcHelper(TypeError, "value must be a 'int' object but received a '%s'", getTypeName(value)); raiseExcHelper(TypeError, "value must be a 'int' object but received a '%s'", getTypeName(value));
bool enable = ((BoxedInt*)value)->n; int n = ((BoxedInt*)value)->n;
if (option_string->s == "ENABLE_INTERPRETER") #define CHECK(_s) \
ENABLE_INTERPRETER = enable; if (option_string->s == STRINGIFY(_s)) \
else if (option_string->s == "ENABLE_OSR") _s = n
ENABLE_OSR = enable;
else if (option_string->s == "ENABLE_REOPT") // :)
ENABLE_REOPT = enable; CHECK(ENABLE_INTERPRETER);
else if (option_string->s == "FORCE_INTERPRETER") else CHECK(ENABLE_OSR);
FORCE_INTERPRETER = enable; else CHECK(ENABLE_REOPT);
else else CHECK(FORCE_INTERPRETER);
raiseExcHelper(ValueError, "unknown option name '%s", option_string->s.c_str()); else CHECK(REOPT_THRESHOLD_INTERPRETER);
else CHECK(OSR_THRESHOLD_INTERPRETER);
else CHECK(REOPT_THRESHOLD_BASELINE);
else CHECK(OSR_THRESHOLD_BASELINE);
else CHECK(SPECULATION_THRESHOLD);
else raiseExcHelper(ValueError, "unknown option name '%s", option_string->s.c_str());
return None; return None;
} }
......
...@@ -2293,7 +2293,7 @@ static CompiledFunction* pickVersion(CLFunction* f, int num_output_args, Box* oa ...@@ -2293,7 +2293,7 @@ static CompiledFunction* pickVersion(CLFunction* f, int num_output_args, Box* oa
} }
FunctionSpecialization* spec = new FunctionSpecialization(UNKNOWN, arg_types); FunctionSpecialization* spec = new FunctionSpecialization(UNKNOWN, arg_types);
EffortLevel::EffortLevel new_effort = initialEffort(); EffortLevel new_effort = initialEffort();
// this also pushes the new CompiledVersion to the back of the version list: // this also pushes the new CompiledVersion to the back of the version list:
chosen_cf = compileFunction(f, spec, new_effort, NULL); chosen_cf = compileFunction(f, spec, new_effort, NULL);
......
# run_args: -n # run_args: -n
# statcheck: ("-O" in EXTRA_JIT_ARGS) or (1 <= stats["OSR exits"] <= 2) # statcheck: ("-O" in EXTRA_JIT_ARGS) or (1 <= stats["num_osr_exits"] <= 2)
# statcheck: stats['slowpath_binop'] <= 10 # statcheck: stats['slowpath_binop'] <= 10
x = 100000 x = 100000
......
# This test could really benefit from defined/settable OSR limits # statcheck: '-O' in EXTRA_JIT_ARGS or 1 <= stats['num_osr_exits'] <= 5
def f(x): def f(x):
def inner(): def inner():
......
# skip-if: '-O' in EXTRA_JIT_ARGS # skip-if: '-O' in EXTRA_JIT_ARGS
# statcheck: 4 <= noninit_count('num_deopt') < 50 # statcheck: 4 <= noninit_count('num_deopt') < 50
# statcheck: 1 <= stats["num_osr_exits"] <= 2
def f(o): try:
print "starting" import __pyston__
__pyston__.setOption("OSR_THRESHOLD_BASELINE", 50)
__pyston__.setOption("REOPT_THRESHOLD_BASELINE", 50)
__pyston__.setOption("SPECULATION_THRESHOLD", 10)
except ImportError:
pass
try: def main():
print o.a def f(o):
if o.b: print "starting"
raise Exception('')
except Exception, e:
print o.c
print e
print o.d
print sorted(locals().items())
print "Done" try:
print o.a
if o.b:
raise Exception('')
except Exception, e:
print o.c
print e
print o.d
print sorted(locals().items())
class C(object): print "Done"
def __repr__(self):
return "<C>"
c = C() class C(object):
c.a = 1 def __repr__(self):
c.b = 0 return "<C>"
c.c = 3
c.d = 4
# These limits are high to try to trigger OSR. c = C()
# TODO we should have some way to lower the OSR thresholds c.a = 1
for i in xrange(20000): c.b = 0
print i c.c = 3
c.d = 4
if i == 5000: for i in xrange(2000):
c.a = [] print i
if i == 6000: if i == 500:
c.b = 1 c.a = []
if i == 7000: if i == 600:
c.c = [] c.b = 1
if i == 8000: if i == 700:
c.b = 0 c.c = []
c.d = 1.0
f(c) if i == 800:
c.b = 0
c.d = 1.0
f(c)
# Regression test reduced from subprocess.py:
import types
def f2(self, args):
if isinstance(args, types.StringTypes):
pass
try: # Regression test reduced from subprocess.py:
self.pid import types
except: def f2(self, args):
pass if isinstance(args, types.StringTypes):
pass
c = C() try:
c.pid = 1 self.pid
for i in xrange(20000): except:
f2(c, None) pass
if i == 15000: c = C()
c.pid = 1.0 c.pid = 1
for i in xrange(2000):
f2(c, None)
if i == 1500:
c.pid = 1.0
main()
# statcheck: ("-O" in EXTRA_JIT_ARGS) or (1 <= stats["num_osr_exits"] <= 2)
# While perhaps not required in practice, we should have the ability to # While perhaps not required in practice, we should have the ability to
# OSR from inside a list comprehension. # OSR from inside a list comprehension.
# statcheck: ("-O" in EXTRA_JIT_ARGS) or (1 <= stats["OSR exits"] <= 2)
def p(i): def p(i):
print i print i
for i in xrange(100000): print len([i for i in xrange(100000) if i % 100 == 0])
pass
# [i for i in xrange(100000) if i % 100 == 0]
# statcheck: '-O' in EXTRA_JIT_ARGS or 1 <= stats['num_osr_exits'] <= 5
# "big osr" in terms of lots of live variables needing to be passed through: # "big osr" in terms of lots of live variables needing to be passed through:
try:
import __pyston__
__pyston__.setOption("OSR_THRESHOLD_INTERPRETER", 5)
__pyston__.setOption("OSR_THRESHOLD_BASELINE", 5)
except ImportError:
pass
def outer(quit): def outer(quit):
if quit: if quit:
return return
...@@ -15,7 +24,7 @@ def outer(quit): ...@@ -15,7 +24,7 @@ def outer(quit):
i = 9 i = 9
l = [] l = []
n = 100000 n = 10000
while n: while n:
n = n - 1 n = n - 1
a = a + 1 a = a + 1
......
# statcheck: '-O' in EXTRA_JIT_ARGS or 1 <= stats['num_osr_exits'] <= 5
# Try to trick the JIT into OSR'ing into an optimized version with a speculation # Try to trick the JIT into OSR'ing into an optimized version with a speculation
# that has already failed. # that has already failed.
# In the f() function, y will have type int, but when we OSR from the while loop, # In the f() function, y will have type int, but when we OSR from the while loop,
...@@ -10,7 +12,7 @@ def xrange(n): ...@@ -10,7 +12,7 @@ def xrange(n):
def f(x): def f(x):
y = xrange(x) y = xrange(x)
n = 100000 n = 10000
while n: while n:
n -= 1 n -= 1
......
# statcheck: '-O' in EXTRA_JIT_ARGS or 1 <= stats['num_osr_exits'] <= 5
# Regression test to make sure we can do an OSR if one of the live variables # Regression test to make sure we can do an OSR if one of the live variables
# is potentially-undefined. # is potentially-undefined.
...@@ -5,10 +7,13 @@ def f(x): ...@@ -5,10 +7,13 @@ def f(x):
if x % 2: if x % 2:
y = x y = x
for i in xrange(1000000): for i in xrange(10000):
pass pass
print y try:
print y
except NameError, e:
print e
f(11) f(11)
f(10) f(10)
# statcheck: '-O' in EXTRA_JIT_ARGS or 1 <= stats['num_osr_exits'] <= 5
# Regression test to make sure we can do an OSR if one of the live variables # Regression test to make sure we can do an OSR if one of the live variables
# is potentially-undefined. # is potentially-undefined.
...@@ -7,10 +9,13 @@ def f(x): ...@@ -7,10 +9,13 @@ def f(x):
xrange(0) xrange(0)
for i in xrange(1000000): for i in xrange(10000):
xrange(0) xrange(0)
print y try:
print y
except NameError, e:
print e
xrange(0) xrange(0)
f(11) f(11)
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
# of stacks that we have: # of stacks that we have:
def recurse(n): def recurse(n):
print n
if n > 0: if n > 0:
return recurse(n - 1) return recurse(n - 1)
return n return n
......
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