Skip to content

Commit fc0c172

Browse files
jbower-fbmeta-codesync[bot]
authored andcommitted
Introduce 3.14t CinderX build
Summary: Gives us a working build of 3.14t + CinderX with no actual support for FT (we don't even provide the FT flag to the module yet). With this change we get to the point we can build + run a very basic smoke test with and without JIT. The work is more than just adding Buck build targets because once we have `Py_GIL_DISABLED` things start breaking due to changes in internal features that we depend on. E.g. the reference counting fields of `PyObject` have changed. To help with this we now have a whole new `borrowed-3.14.free-threading.c.template` (which isn't called 3.14t for Buck-related reasons). Some stuff here we were going to need to fix anyway, but most implementation is incomplete so I've filled tasks for all the todos. Reviewed By: alexmalyshev Differential Revision: D90686232 fbshipit-source-id: 03b2961422a8ab0c7565adf3ab2ef4872f34d3f3
1 parent b81289d commit fc0c172

23 files changed

Lines changed: 9715 additions & 47 deletions

cinderx/Common/py-portability.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ inline PyObject* frameExecutable(_PyInterpreterFrame* frame) {
240240
*_tmp_dst_ptr = PyStackRef_FromPyObjectSteal(VAL); \
241241
PyStackRef_XCLOSE(_tmp_old_dst); \
242242
} while (0)
243-
#define Ci_STACK_NEWREF(VAL) _PyStackRef_FromPyObjectNew(VAL)
243+
#define Ci_STACK_NEWREF(VAL) PyStackRef_FromPyObjectNew(VAL)
244244
#define Ci_STACK_CLOSE(VAL) PyStackRef_CLOSE(VAL)
245245
#define Ci_STACK_XCLOSE(VAL) PyStackRef_XCLOSE(VAL)
246246

cinderx/Common/string.h

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,32 @@
66

77
#include "cinderx/Common/py-portability.h"
88

9-
// Create a function static variable for Python string.
10-
// This string is explicitly immortalized, but not
11-
// interned because doing so will cause it to be released
12-
// by the runtime at shutdown.
9+
// Create a function static variable for Python string. This string is
10+
// explicitly immortalized, but not interned because doing so will cause it to
11+
// be released by the runtime at shutdown. We cannot use
12+
// _Py_SetImmortalUntracked() as this has an assertion to force the use of
13+
// _PyUnicode_InternImmortal() for strings.
14+
#if PY_VERSION_HEX >= 0x030E0000 && defined(Py_GIL_DISABLED)
15+
#define DEFINE_NAMED_STATIC_STRING(NAME, STR) \
16+
static PyObject* NAME = NULL; \
17+
if (NAME == NULL) { \
18+
PyObject* op = PyUnicode_FromString(STR); \
19+
op->ob_tid = _Py_UNOWNED_TID; \
20+
op->ob_ref_local = _Py_IMMORTAL_REFCNT_LOCAL; \
21+
op->ob_ref_shared = 0; \
22+
_Py_atomic_or_uint8(&op->ob_gc_bits, _PyGC_BITS_DEFERRED); \
23+
_PyASCIIObject_CAST(op)->state.statically_allocated = 1; \
24+
NAME = op; \
25+
}
26+
#else
1327
#define DEFINE_NAMED_STATIC_STRING(NAME, STR) \
1428
static PyObject* NAME = NULL; \
1529
if (NAME == NULL) { \
1630
PyObject* new_str = PyUnicode_FromString(STR); \
1731
new_str->ob_refcnt = 0x3fffffff; \
1832
NAME = new_str; \
1933
}
34+
#endif
2035

2136
// Shorter variant of DEFINE_NAMED_STATIC_STRING.
2237
#define DEFINE_STATIC_STRING(STR) DEFINE_NAMED_STATIC_STRING(s_##STR, (#STR))

cinderx/Immortalize/immortalize.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,13 @@ bool immortalize(PyObject* obj) {
7474
}
7575

7676
#if PY_VERSION_HEX >= 0x030C0000
77-
PyObject* immortalize_heap(PyObject* mod) {
77+
PyObject* immortalize_heap([[maybe_unused]] PyObject* mod) {
78+
#ifdef Py_GIL_DISABLED
79+
PyErr_SetString(
80+
PyExc_RuntimeError,
81+
"Immortalizing the heap is not yet supported in FT Python");
82+
#else
83+
// TODO(T251571267): Low priority for now.
7884
/* Remove any dead objects to avoid immortalizing them */
7985
PyGC_Collect();
8086

@@ -95,8 +101,10 @@ PyObject* immortalize_heap(PyObject* mod) {
95101
Py_TYPE(FROM_GC(gc))
96102
->tp_traverse(FROM_GC(gc), immortalize_visitor, nullptr);
97103
}
104+
#endif
98105

99106
Py_RETURN_NONE;
107+
}
100108
#else
101109
PyObject* immortalize_heap(PyObject* /* mod */) {
102110
// for 3.10.cinder, we fall back to the implementation that ships in the gc
@@ -112,5 +120,5 @@ PyObject* immortalize_heap(PyObject* /* mod */) {
112120
return nullptr;
113121
}
114122
return Ref<>::steal(PyObject_CallFunctionObjArgs(immortalize, nullptr));
115-
#endif
116123
}
124+
#endif

cinderx/Interpreter/3.14/Includes/generated_cases.c.h

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9445,6 +9445,13 @@
94459445
_PyStackRef v;
94469446
v = stack_pointer[-1];
94479447
list = stack_pointer[-2 - (oparg-1)];
9448+
#if Py_GIL_DISABLED
9449+
int err = _PyList_AppendTakeRef((PyListObject *)PyStackRef_AsPyObjectBorrow(list),
9450+
PyStackRef_AsPyObjectSteal(v));
9451+
if (err < 0) {
9452+
JUMP_TO_LABEL(pop_1_error);
9453+
}
9454+
#else
94489455
_PyFrame_SetStackPointer(frame, stack_pointer);
94499456
int err = Ci_ListOrCheckedList_Append(
94509457
(PyListObject*)PyStackRef_AsPyObjectBorrow(list), PyStackRef_AsPyObjectBorrow(v));
@@ -9457,6 +9464,10 @@
94579464
if (err < 0) {
94589465
JUMP_TO_LABEL(error);
94599466
}
9467+
stack_pointer += 1;
9468+
#endif
9469+
stack_pointer += -1;
9470+
assert(WITHIN_STACK_BOUNDS());
94609471
DISPATCH();
94619472
}
94629473

@@ -11556,6 +11567,20 @@
1155611567
value = stack_pointer[-1];
1155711568
key = stack_pointer[-2];
1155811569
dict_st = stack_pointer[-3 - (oparg - 1)];
11570+
#if Py_GIL_DISABLED
11571+
PyObject *dict = PyStackRef_AsPyObjectBorrow(dict_st);
11572+
assert(PyDict_CheckExact(dict));
11573+
_PyFrame_SetStackPointer(frame, stack_pointer);
11574+
int err = _PyDict_SetItem_Take2(
11575+
(PyDictObject *)dict,
11576+
PyStackRef_AsPyObjectSteal(key),
11577+
PyStackRef_AsPyObjectSteal(value)
11578+
);
11579+
stack_pointer = _PyFrame_GetStackPointer(frame);
11580+
if (err != 0) {
11581+
JUMP_TO_LABEL(pop_2_error);
11582+
}
11583+
#else
1155911584
PyObject *dict = PyStackRef_AsPyObjectBorrow(dict_st);
1156011585
_PyFrame_SetStackPointer(frame, stack_pointer);
1156111586
int err = Ci_DictOrChecked_SetItem(dict,
@@ -11572,9 +11597,13 @@
1157211597
_PyFrame_SetStackPointer(frame, stack_pointer);
1157311598
PyStackRef_CLOSE(key);
1157411599
stack_pointer = _PyFrame_GetStackPointer(frame);
11600+
stack_pointer += 2;
11601+
#endif
1157511602
if (err != 0) {
11576-
JUMP_TO_LABEL(error);
11603+
JUMP_TO_LABEL(pop_2_error);
1157711604
}
11605+
stack_pointer += -2;
11606+
assert(WITHIN_STACK_BOUNDS());
1157811607
DISPATCH();
1157911608
}
1158011609

cinderx/Interpreter/3.14/cinder-bytecodes.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,21 +466,42 @@ dummy_func(
466466
}
467467

468468
override inst(MAP_ADD, (dict_st, unused[oparg - 1], key, value -- dict_st, unused[oparg - 1])) {
469+
#ifdef Py_GIL_DISABLED
470+
// T250369690: Need thread-safe checked collections
471+
PyObject *dict = PyStackRef_AsPyObjectBorrow(dict_st);
472+
assert(PyDict_CheckExact(dict));
473+
/* dict[key] = value */
474+
// Do not DECREF INPUTS because the function steals the references
475+
int err = _PyDict_SetItem_Take2(
476+
(PyDictObject *)dict,
477+
PyStackRef_AsPyObjectSteal(key),
478+
PyStackRef_AsPyObjectSteal(value)
479+
);
480+
ERROR_IF(err != 0);
481+
#else
469482
PyObject *dict = PyStackRef_AsPyObjectBorrow(dict_st);
470483
/* dict[key] = value */
471484
int err = Ci_DictOrChecked_SetItem(dict,
472485
PyStackRef_AsPyObjectBorrow(key),
473486
PyStackRef_AsPyObjectBorrow(value));
474487
PyStackRef_CLOSE(value);
475488
PyStackRef_CLOSE(key);
489+
#endif
476490
ERROR_IF(err != 0);
477491
}
478492

479493
override inst(LIST_APPEND, (list, unused[oparg-1], v -- list, unused[oparg-1])) {
494+
#ifdef Py_GIL_DISABLED
495+
// T250369690: Need thread-safe checked collections
496+
int err = _PyList_AppendTakeRef((PyListObject *)PyStackRef_AsPyObjectBorrow(list),
497+
PyStackRef_AsPyObjectSteal(v));
498+
ERROR_IF(err < 0);
499+
#else
480500
int err = Ci_ListOrCheckedList_Append(
481501
(PyListObject*)PyStackRef_AsPyObjectBorrow(list), PyStackRef_AsPyObjectBorrow(v));
482502
PyStackRef_CLOSE(v);
483503
ERROR_IF(err < 0);
504+
#endif
484505
}
485506

486507
override inst(EXTENDED_OPCODE, (args[oparg>>2] -- top[oparg&0x03])) {

cinderx/Jit/codegen/frame_asm.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,8 @@ void FrameAsm::emitIncTotalRefCount(const arch::Gp& scratch_reg) {
153153
#endif
154154
}
155155

156+
#ifdef ENABLE_LIGHTWEIGHT_FRAMES
157+
// TODO(T251571746): need to correctly implement FT incref.
156158
void FrameAsm::incRef(const arch::Gp& reg, const arch::Gp& scratch_reg) {
157159
#if defined(CINDER_X86_64)
158160
as_->mov(scratch_reg, x86::ptr(reg, offsetof(PyObject, ob_refcnt)));
@@ -171,6 +173,7 @@ void FrameAsm::incRef(const arch::Gp& reg, const arch::Gp& scratch_reg) {
171173
CINDER_UNSUPPORTED
172174
#endif
173175
}
176+
#endif
174177

175178
bool FrameAsm::storeConst(
176179
const arch::Gp& reg,
@@ -358,6 +361,8 @@ void FrameAsm::linkLightWeightFunctionFrame(
358361
preserver.remap();
359362
}
360363
#else
364+
// TODO(T251571746): For FTPython either need to set TLBC somewhere in here or
365+
// in the reifier.
361366
throw std::runtime_error{
362367
"linkLightWeightFunctionFrame: Lightweight frames are not supported"};
363368
#endif

cinderx/Jit/config.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,14 @@ struct Config {
145145
// dictionaries (but not their contents).
146146
bool stable_frame{true};
147147
// Use inline caches for attribute accesses.
148-
bool attr_caches{true};
148+
bool attr_caches{
149+
#ifdef Py_GIL_DISABLED
150+
// TODO(T250369692): FT support for inline-caches.
151+
false
152+
#else
153+
true
154+
#endif
155+
};
149156
// Collect stats information about attribute caches.
150157
bool collect_attr_cache_stats{false};
151158
// Use type annotations to create runtime checks.

cinderx/Jit/deopt.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
#include "cinderx/Jit/deopt.h"
44

5+
// clang-tidy off
6+
#include "cinderx/UpstreamBorrow/borrowed.h"
7+
// clang-tidy on
8+
9+
#include "internal/pycore_ceval.h"
10+
511
#include "cinderx/Common/py-portability.h"
612
#include "cinderx/Common/util.h"
713
#include "cinderx/Jit/bytecode_offsets.h"
@@ -313,11 +319,18 @@ static void reifyFrameImpl(
313319
const uint64_t* regs) {
314320
#if PY_VERSION_HEX >= 0x030E0000
315321
BorrowedRef<PyCodeObject> code_obj = frameCode(frame);
322+
#ifdef Py_GIL_DISABLED
323+
PyThreadState* tstate = _PyThreadState_GET();
324+
frame->instr_ptr = _PyEval_GetExecutableCode(tstate, _PyFrame_GetCode(frame));
325+
frame->tlbc_index = reinterpret_cast<_PyThreadStateImpl*>(tstate)->tlbc_index;
326+
#else
327+
frame->instr_ptr = _PyCode_CODE(code_obj);
328+
#endif
316329
int cause_instr_idx = frame_meta.cause_instr_idx.value();
317330
// Resume with instr_ptr pointing to the cause instruction if we are entering
318331
// the interpreter to re-run a failed instruction, or implement an instruction
319332
// we don't JIT.
320-
frame->instr_ptr = _PyCode_CODE(code_obj) + cause_instr_idx;
333+
frame->instr_ptr += cause_instr_idx;
321334
if (&frame_meta != &meta.innermostFrame()) {
322335
// If we're not the inner most frame then we're always deopting
323336
// after the instruction that executed

0 commit comments

Comments
 (0)