Skip to content

Commit 5ec5965

Browse files
alexmalyshevmeta-codesync[bot]
authored andcommitted
Replace PYJIT_RESULT_RETRY with more explicit status codes
Summary: Making it clearer what happened in the JIT when it was unable to compile a function. Having a general "retry" error is meaningless to the user, it should instead say what happened and then the user can decide whether to retry or not. At the same time, consolidate the code between tryCompile() and forcedJitVectorcall(). There's really only four different codepaths to take after compileFunction() is called, we don't need a full switch over everything and it's cleaner without it. Reviewed By: yoney Differential Revision: D91380049 fbshipit-source-id: 0abadc3ad0b3ec56cdc45132461b545b42252f18
1 parent e8ba0ae commit 5ec5965

2 files changed

Lines changed: 44 additions & 54 deletions

File tree

cinderx/Jit/pyjit.cpp

Lines changed: 39 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -137,16 +137,6 @@ bool isPreloaded(BorrowedRef<PyFunctionObject> func) {
137137
return hir::preloaderManager().find(func) != nullptr;
138138
}
139139

140-
_PyJIT_Result tryCompile(BorrowedRef<PyFunctionObject> func) {
141-
_PyJIT_Result result = compileFunction(func);
142-
// Reset the function back to the interpreter if there was any non-retryable
143-
// failure.
144-
if (result != PYJIT_RESULT_OK && result != PYJIT_RESULT_RETRY) {
145-
func->vectorcall = getInterpretedVectorcall(func);
146-
}
147-
return result;
148-
}
149-
150140
void incrementShadowcodeCall([[maybe_unused]] BorrowedRef<PyCodeObject> code) {
151141
#if SHADOWCODE_SUPPORTED
152142
// The interpreter will only increment up to the shadowcode threshold
@@ -172,46 +162,35 @@ PyObject* forcedJitVectorcall(
172162
BorrowedRef<PyFunctionObject> func{func_obj};
173163
BorrowedRef<PyCodeObject> code{func->func_code};
174164

175-
_PyJIT_Result result = tryCompile(func);
165+
_PyJIT_Result result = compileFunction(func);
176166
if (result == PYJIT_RESULT_OK) {
177167
JIT_DCHECK(
178168
isJitCompiled(func),
179169
"JIT succeeded for function {} but it is not recognized as compiled",
180170
funcFullname(func));
181-
} else {
182-
JIT_DCHECK(
183-
!isJitCompiled(func),
184-
"JIT failed (error: {}) for function {} but it seems to have been "
185-
"compiled",
186-
result,
187-
funcFullname(func));
171+
return func->vectorcall(func_obj, stack, nargsf, kwnames);
188172
}
189173

190-
switch (result) {
191-
case PYJIT_RESULT_OK:
192-
return func->vectorcall(func_obj, stack, nargsf, kwnames);
193-
case PYJIT_RESULT_RETRY: {
194-
incrementShadowcodeCall(code);
195-
auto entry = getInterpretedVectorcall(func);
196-
return entry(func_obj, stack, nargsf, kwnames);
197-
}
198-
case PYJIT_RESULT_CANNOT_SPECIALIZE:
199-
case PYJIT_RESULT_NOT_ON_JITLIST:
200-
case PYJIT_NOT_INITIALIZED:
201-
case PYJIT_RESULT_NO_PRELOADER:
202-
case PYJIT_RESULT_UNKNOWN_ERROR:
203-
case PYJIT_OVER_MAX_CODE_SIZE:
204-
return func->vectorcall(func_obj, stack, nargsf, kwnames);
205-
case PYJIT_RESULT_PYTHON_EXCEPTION:
206-
return nullptr;
207-
default:
208-
break;
174+
auto interp_entry = getInterpretedVectorcall(func);
175+
176+
// Python errors shouldn't happen during compilation, but if they do, bubble
177+
// them up without calling the function.
178+
if (result == PYJIT_RESULT_PYTHON_EXCEPTION) {
179+
func->vectorcall = interp_entry;
180+
return nullptr;
209181
}
210182

211-
JIT_ABORT(
212-
"Unrecognized JIT result code {} for function {}",
213-
static_cast<int>(result),
214-
funcFullname(func));
183+
// Reset the function's entrypoint if it doesn't seem like there's a chance
184+
// compilation will work "soon".
185+
if (result != PYJIT_RESULT_ALREADY_SCHEDULED &&
186+
result != PYJIT_RESULT_PAUSED) {
187+
func->vectorcall = interp_entry;
188+
}
189+
190+
// There's been some kind of compilation error, explicitly call the
191+
// interpreted entrypoint instead.
192+
incrementShadowcodeCall(code);
193+
return interp_entry(func_obj, stack, nargsf, kwnames);
215194
}
216195

217196
// Python function entry point when the JIT is enabled.
@@ -997,7 +976,7 @@ void compile_worker_thread() {
997976
while (BorrowedRef<> unit = getThreadedCompileContext().nextUnit()) {
998977
attempts++;
999978
_PyJIT_Result res = tryCompilePreloaded(unit);
1000-
if (res == PYJIT_RESULT_RETRY) {
979+
if (res == PYJIT_RESULT_ALREADY_SCHEDULED) {
1001980
retries++;
1002981
getThreadedCompileContext().retryUnit(unit);
1003982
}
@@ -1592,23 +1571,30 @@ PyObject* force_compile(PyObject* /* self */, PyObject* arg) {
15921571
if (func == nullptr) {
15931572
return nullptr;
15941573
}
1595-
15961574
if (!isJitUsable() || isJitCompiled(func)) {
15971575
Py_RETURN_FALSE;
15981576
}
15991577

1600-
switch (compileFunction(func)) {
1578+
_PyJIT_Result result = compileFunction(func);
1579+
switch (result) {
16011580
case PYJIT_RESULT_OK:
16021581
Py_RETURN_TRUE;
1582+
case PYJIT_RESULT_ALREADY_SCHEDULED:
1583+
// Strange case, the function is being compiled by a different thread.
1584+
// Shouldn't happen, but don't die if it does.
1585+
Py_RETURN_FALSE;
1586+
case PYJIT_RESULT_PAUSED:
1587+
PyErr_SetString(
1588+
PyExc_RuntimeError,
1589+
"Compilation failed because the JIT was paused, but that shouldn't "
1590+
"be possible as this case was already checked");
1591+
return nullptr;
16031592
case PYJIT_RESULT_CANNOT_SPECIALIZE:
16041593
PyErr_SetString(PyExc_RuntimeError, "PYJIT_RESULT_CANNOT_SPECIALIZE");
16051594
return nullptr;
16061595
case PYJIT_RESULT_NOT_ON_JITLIST:
16071596
PyErr_SetString(PyExc_RuntimeError, "PYJIT_RESULT_NOT_ON_JITLIST");
16081597
return nullptr;
1609-
case PYJIT_RESULT_RETRY:
1610-
PyErr_SetString(PyExc_RuntimeError, "PYJIT_RESULT_RETRY");
1611-
return nullptr;
16121598
case PYJIT_RESULT_UNKNOWN_ERROR:
16131599
PyErr_SetString(PyExc_RuntimeError, "PYJIT_RESULT_UNKNOWN_ERROR");
16141600
return nullptr;
@@ -1624,7 +1610,10 @@ PyObject* force_compile(PyObject* /* self */, PyObject* arg) {
16241610
case PYJIT_RESULT_PYTHON_EXCEPTION:
16251611
return nullptr;
16261612
}
1627-
PyErr_SetString(PyExc_RuntimeError, "Unhandled compilation result");
1613+
PyErr_Format(
1614+
PyExc_RuntimeError,
1615+
"Unhandled compilation result: %d",
1616+
static_cast<int>(result));
16281617
return nullptr;
16291618
}
16301619

@@ -3516,7 +3505,7 @@ _PyJIT_Result compileFunction(BorrowedRef<PyFunctionObject> func) {
35163505
return PYJIT_NOT_INITIALIZED;
35173506
}
35183507
if (isJitPaused()) {
3519-
return PYJIT_RESULT_RETRY;
3508+
return PYJIT_RESULT_PAUSED;
35203509
}
35213510
if (!isJitUsable()) {
35223511
return PYJIT_RESULT_UNKNOWN_ERROR;
@@ -3724,7 +3713,7 @@ Context::CompilationResult compilePreloaderImpl(
37243713
return {compiled, PYJIT_RESULT_OK};
37253714
}
37263715
if (!jit_ctx->addActiveCompile(key)) {
3727-
return {nullptr, PYJIT_RESULT_RETRY};
3716+
return {nullptr, PYJIT_RESULT_ALREADY_SCHEDULED};
37283717
}
37293718
}
37303719

cinderx/Jit/pyjit_result.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,11 @@ typedef enum {
2727
/* Someone tried to compile a function but the JIT is not initialized. */
2828
PYJIT_NOT_INITIALIZED,
2929

30-
/* During threaded compile we may end compiling the same code twice in
31-
different contexts. If you get this response, you should retry later
32-
or give up as best fits the case. */
33-
PYJIT_RESULT_RETRY,
30+
/* Function is being scheduled for compilation across multiple threads. */
31+
PYJIT_RESULT_ALREADY_SCHEDULED,
32+
33+
/* Compilation didn't happen because the JIT is currently paused. */
34+
PYJIT_RESULT_PAUSED,
3435

3536
/* We are compiling with preload required, but did not find a preloader. */
3637
PYJIT_RESULT_NO_PRELOADER,

0 commit comments

Comments
 (0)