Commit b4094e4e authored by Kevin Modzelewski's avatar Kevin Modzelewski

Remove function versions that fail their speculations

The goal is to not continually call functions that deopt every time,
since the deopt is expensive.

Right now the threshold is simple: if a function deopts 4 (configurable)
times, then mark that function version as invalid and force a recompilation
on the next call.
parent 25ac9de4
......@@ -280,6 +280,42 @@ void compileAndRunModule(AST_Module* m, BoxedModule* bm) {
((void (*)())cf->code)();
}
// If a function version keeps failing its speculations, kill it (remove it
// from the list of valid function versions). The next time we go to call
// the function, we will have to pick a different version, potentially recompiling.
//
// TODO we should have logic like this at the CLFunc level that detects that we keep
// on creating functions with failing speculations, and then stop speculating.
void CompiledFunction::speculationFailed() {
LOCK_REGION(codegen_rwlock.asWrite());
this->times_speculation_failed++;
if (this->times_speculation_failed >= 4) {
// printf("Killing %p because it failed too many speculations\n", this);
CLFunction* cl = this->clfunc;
assert(cl);
bool found = false;
for (int i = 0; i < clfunc->versions.size(); i++) {
if (clfunc->versions[i] == this) {
clfunc->versions.erase(clfunc->versions.begin() + i);
this->dependent_callsites.invalidateAll();
found = true;
break;
}
}
if (!found) {
for (int i = 0; i < clfunc->versions.size(); i++) {
printf("%p\n", clfunc->versions[i]);
}
}
assert(found);
}
}
/// Reoptimizes the given function version at the new effort level.
/// 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.
......
......@@ -192,7 +192,7 @@ public:
EffortLevel::EffortLevel effort;
int64_t times_called;
int64_t times_called, times_speculation_failed;
ICInvalidator dependent_callsites;
LocationMap* location_map; // only meaningful if this is a compiled frame
......@@ -203,13 +203,17 @@ public:
llvm::Value* llvm_code, EffortLevel::EffortLevel effort,
const OSREntryDescriptor* entry_descriptor)
: 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), location_map(nullptr) {}
code(code), llvm_code(llvm_code), effort(effort), times_called(0), times_speculation_failed(0),
location_map(nullptr) {}
// TODO this will need to be implemented eventually; things to delete:
// - line_table if it exists
// - location_map if it exists
// - all entries in ics (after deregistering them)
~CompiledFunction();
// Call this when a speculation inside this version failed
void speculationFailed();
};
class BoxedModule;
......
......@@ -233,6 +233,10 @@ extern "C" Box* deopt(AST_expr* expr, Box* value) {
auto locals = getLocals(false /* filter */);
auto execution_point = getExecutionPoint();
// Should we only do this selectively?
execution_point.cf->speculationFailed();
return astInterpretFrom(execution_point.cf, expr, execution_point.current_stmt, value, locals);
}
......
# statcheck: 0 <= noninit_count('num_deopt') < 500
# skip-if: '-O' in EXTRA_JIT_ARGS
# statcheck: 4 <= noninit_count('num_deopt') < 50
def f(o):
print "starting"
......@@ -6,16 +7,19 @@ def f(o):
try:
print o.a
if o.b:
raise Exception()
raise Exception('')
except Exception, e:
print o.c
print e
print o.d
print sorted(locals().items())
print "Done"
class C(object):
pass
def __repr__(self):
return "<C>"
c = C()
c.a = 1
c.b = 0
......@@ -25,6 +29,8 @@ c.d = 4
# These limits are high to try to trigger OSR.
# TODO we should have some way to lower the OSR thresholds
for i in xrange(20000):
print i
if i == 5000:
c.a = []
......@@ -39,3 +45,24 @@ for i in xrange(20000):
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:
self.pid
except:
pass
c = C()
c.pid = 1
for i in xrange(20000):
f2(c, None)
if i == 15000:
c.pid = 1.0
import types
def f(self, args):
if isinstance(args, types.StringTypes):
pass
try:
self.pid
except:
pass
class C(object):
pass
c = C()
c.pid = 1
for i in xrange(20000):
f(c, None)
if i == 15000:
c.pid = 1.0
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