https://github.com/python/cpython/commit/a1aeec61c4321ba9a6966109343bd88dcf9cb26a
commit: a1aeec61c4321ba9a6966109343bd88dcf9cb26a
branch: main
author: Mark Shannon <m...@hotpy.org>
committer: markshannon <m...@hotpy.org>
date: 2025-03-17T09:19:04Z
summary:

GH-131238: Core header refactor (GH-131250)

* Moves most structs in pycore_ header files into pycore_structs.h and 
pycore_runtime_structs.h

* Removes many cross-header dependencies

files:
A Include/internal/pycore_interp_structs.h
A Include/internal/pycore_runtime_structs.h
A Include/internal/pycore_structs.h
M Include/cpython/code.h
M Include/cpython/object.h
M Include/internal/pycore_atexit.h
M Include/internal/pycore_backoff.h
M Include/internal/pycore_ceval_state.h
M Include/internal/pycore_code.h
M Include/internal/pycore_codecs.h
M Include/internal/pycore_context.h
M Include/internal/pycore_dtoa.h
M Include/internal/pycore_fileutils.h
M Include/internal/pycore_floatobject.h
M Include/internal/pycore_function.h
M Include/internal/pycore_gc.h
M Include/internal/pycore_global_objects.h
M Include/internal/pycore_global_strings.h
M Include/internal/pycore_hamt.h
M Include/internal/pycore_import.h
M Include/internal/pycore_index_pool.h
M Include/internal/pycore_instruments.h
M Include/internal/pycore_interp.h
M Include/internal/pycore_long.h
M Include/internal/pycore_parser.h
M Include/internal/pycore_pyhash.h
M Include/internal/pycore_pymem.h
M Include/internal/pycore_pystate.h
M Include/internal/pycore_pythread.h
M Include/internal/pycore_runtime.h
M Include/internal/pycore_runtime_init.h
M Include/internal/pycore_stackref.h
M Include/internal/pycore_time.h
M Include/internal/pycore_typeobject.h
M Include/internal/pycore_unicodeobject.h
M Include/internal/pycore_uniqueid.h
M Include/internal/pycore_warnings.h
M Makefile.pre.in
M Modules/_codecsmodule.c
M Modules/_cursesmodule.c
M Modules/_tkinter.c
M Objects/genobject.c
M Objects/listobject.c
M Objects/object.c
M Objects/unicodeobject.c
M Parser/pegen.c
M Python/bootstrap_hash.c
M Python/dtoa.c
M Python/formatter_unicode.c
M Python/initconfig.c
M Python/instrumentation.c
M Python/interpconfig.c
M Python/intrinsics.c
M Python/pylifecycle.c
M Python/pystate.c
M Python/suggestions.c
M Tools/build/generate_global_objects.py

diff --git a/Include/cpython/code.h b/Include/cpython/code.h
index 2bd3e08631f0ad..3f0dce03455526 100644
--- a/Include/cpython/code.h
+++ b/Include/cpython/code.h
@@ -8,25 +8,6 @@
 extern "C" {
 #endif
 
-/* Total tool ids available */
-#define  _PY_MONITORING_TOOL_IDS 8
-/* Count of all local monitoring events */
-#define  _PY_MONITORING_LOCAL_EVENTS 11
-/* Count of all "real" monitoring events (not derived from other events) */
-#define _PY_MONITORING_UNGROUPED_EVENTS 16
-/* Count of all  monitoring events */
-#define _PY_MONITORING_EVENTS 19
-
-/* Tables of which tools are active for each monitored event. */
-typedef struct _Py_LocalMonitors {
-    uint8_t tools[_PY_MONITORING_LOCAL_EVENTS];
-} _Py_LocalMonitors;
-
-typedef struct _Py_GlobalMonitors {
-    uint8_t tools[_PY_MONITORING_UNGROUPED_EVENTS];
-} _Py_GlobalMonitors;
-
-
 typedef struct {
     PyObject *_co_code;
     PyObject *_co_varnames;
@@ -34,44 +15,12 @@ typedef struct {
     PyObject *_co_freevars;
 } _PyCoCached;
 
-/* Ancillary data structure used for instrumentation.
-   Line instrumentation creates this with sufficient
-   space for one entry per code unit. The total size
-   of the data will be `bytes_per_entry * Py_SIZE(code)` */
-typedef struct {
-    uint8_t bytes_per_entry;
-    uint8_t data[1];
-} _PyCoLineInstrumentationData;
-
-
 typedef struct {
     int size;
     int capacity;
     struct _PyExecutorObject *executors[1];
 } _PyExecutorArray;
 
-/* Main data structure used for instrumentation.
- * This is allocated when needed for instrumentation
- */
-typedef struct {
-    /* Monitoring specific to this code object */
-    _Py_LocalMonitors local_monitors;
-    /* Monitoring that is active on this code object */
-    _Py_LocalMonitors active_monitors;
-    /* The tools that are to be notified for events for the matching code unit 
*/
-    uint8_t *tools;
-    /* The version of tools when they instrument the code */
-    uintptr_t tool_versions[_PY_MONITORING_TOOL_IDS];
-    /* Information to support line events */
-    _PyCoLineInstrumentationData *lines;
-    /* The tools that are to be notified for line events for the matching code 
unit */
-    uint8_t *line_tools;
-    /* Information to support instruction events */
-    /* The underlying instructions, which can themselves be instrumented */
-    uint8_t *per_instruction_opcodes;
-    /* The tools that are to be notified for instruction events for the 
matching code unit */
-    uint8_t *per_instruction_tools;
-} _PyCoMonitoringData;
 
 #ifdef Py_GIL_DISABLED
 
@@ -151,7 +100,7 @@ typedef struct {
     _PyExecutorArray *co_executors;      /* executors from optimizer */        
\
     _PyCoCached *_co_cached;      /* cached co_* attributes */                 
\
     uintptr_t _co_instrumentation_version; /* current instrumentation version 
*/ \
-    _PyCoMonitoringData *_co_monitoring; /* Monitoring data */                 
\
+    struct _PyCoMonitoringData *_co_monitoring; /* Monitoring data */          
\
     Py_ssize_t _co_unique_id;     /* ID used for per-thread refcounting */   \
     int _co_firsttraceable;       /* index of first traceable instruction */   
\
     /* Scratch space for extra data relating to the code object.               
\
diff --git a/Include/cpython/object.h b/Include/cpython/object.h
index 184aa63b3a5ed1..e2300aee7a207a 100644
--- a/Include/cpython/object.h
+++ b/Include/cpython/object.h
@@ -512,7 +512,6 @@ PyAPI_FUNC(int) PyObject_VisitManagedDict(PyObject *obj, 
visitproc visit, void *
 PyAPI_FUNC(int) _PyObject_SetManagedDict(PyObject *obj, PyObject *new_dict);
 PyAPI_FUNC(void) PyObject_ClearManagedDict(PyObject *obj);
 
-#define TYPE_MAX_WATCHERS 8
 
 typedef int(*PyType_WatchCallback)(PyTypeObject *);
 PyAPI_FUNC(int) PyType_AddWatcher(PyType_WatchCallback callback);
diff --git a/Include/internal/pycore_atexit.h b/Include/internal/pycore_atexit.h
index db1e5568e09413..4d4c455cc4b24f 100644
--- a/Include/internal/pycore_atexit.h
+++ b/Include/internal/pycore_atexit.h
@@ -12,44 +12,6 @@ extern "C" {
 #endif
 
 
-//###############
-// runtime atexit
-
-typedef void (*atexit_callbackfunc)(void);
-
-struct _atexit_runtime_state {
-    PyMutex mutex;
-#define NEXITFUNCS 32
-    atexit_callbackfunc callbacks[NEXITFUNCS];
-    int ncallbacks;
-};
-
-
-//###################
-// interpreter atexit
-
-typedef void (*atexit_datacallbackfunc)(void *);
-
-typedef struct atexit_callback {
-    atexit_datacallbackfunc func;
-    void *data;
-    struct atexit_callback *next;
-} atexit_callback;
-
-struct atexit_state {
-#ifdef Py_GIL_DISABLED
-    PyMutex ll_callbacks_lock;
-#endif
-    atexit_callback *ll_callbacks;
-
-    // XXX The rest of the state could be moved to the atexit module state
-    // and a low-level callback added for it during module exec.
-    // For the moment we leave it here.
-
-    // List containing tuples with callback information.
-    // e.g. [(func, args, kwargs), ...]
-    PyObject *callbacks;
-};
 
 #ifdef Py_GIL_DISABLED
 #  define _PyAtExit_LockCallbacks(state) 
PyMutex_Lock(&state->ll_callbacks_lock);
diff --git a/Include/internal/pycore_backoff.h 
b/Include/internal/pycore_backoff.h
index b5e33fa8b7abc0..6291afe9e7b4f8 100644
--- a/Include/internal/pycore_backoff.h
+++ b/Include/internal/pycore_backoff.h
@@ -10,14 +10,7 @@ extern "C" {
 #endif
 
 #include <assert.h>
-#include <stdbool.h>
-#include <stdint.h>
-
-
-typedef struct {
-    uint16_t value_and_backoff;
-} _Py_BackoffCounter;
-
+#include "pycore_structs.h" // _Py_BackoffCounter
 
 /* 16-bit countdown counters using exponential backoff.
 
diff --git a/Include/internal/pycore_ceval_state.h 
b/Include/internal/pycore_ceval_state.h
index 009a1ea41eb985..8077c8c68819e3 100644
--- a/Include/internal/pycore_ceval_state.h
+++ b/Include/internal/pycore_ceval_state.h
@@ -12,16 +12,6 @@ extern "C" {
 #include "pycore_gil.h"             // struct _gil_runtime_state
 
 
-typedef int (*_Py_pending_call_func)(void *);
-
-struct _pending_call {
-    _Py_pending_call_func func;
-    void *arg;
-    int flags;
-};
-
-#define PENDINGCALLSARRAYSIZE 300
-
 #define MAXPENDINGCALLS PENDINGCALLSARRAYSIZE
 /* For interpreter-level pending calls, we want to avoid spending too
    much time on pending calls in any one thread, so we apply a limit. */
@@ -40,69 +30,6 @@ struct _pending_call {
    pending calls for the main thread. */
 #define MAXPENDINGCALLSLOOP_MAIN 0
 
-struct _pending_calls {
-    PyThreadState *handling_thread;
-    PyMutex mutex;
-    /* Request for running pending calls. */
-    int32_t npending;
-    /* The maximum allowed number of pending calls.
-       If the queue fills up to this point then _PyEval_AddPendingCall()
-       will return _Py_ADD_PENDING_FULL. */
-    int32_t max;
-    /* We don't want a flood of pending calls to interrupt any one thread
-       for too long, so we keep a limit on the number handled per pass.
-       A value of 0 means there is no limit (other than the maximum
-       size of the list of pending calls). */
-    int32_t maxloop;
-    struct _pending_call calls[PENDINGCALLSARRAYSIZE];
-    int first;
-    int next;
-};
-
-
-typedef enum {
-    PERF_STATUS_FAILED = -1,  // Perf trampoline is in an invalid state
-    PERF_STATUS_NO_INIT = 0,  // Perf trampoline is not initialized
-    PERF_STATUS_OK = 1,       // Perf trampoline is ready to be executed
-} perf_status_t;
-
-#ifdef PY_HAVE_PERF_TRAMPOLINE
-struct code_arena_st;
-
-struct trampoline_api_st {
-    void* (*init_state)(void);
-    void (*write_state)(void* state, const void *code_addr,
-                        unsigned int code_size, PyCodeObject* code);
-    int (*free_state)(void* state);
-    void *state;
-    Py_ssize_t code_padding;
-};
-#endif
-
-
-struct _ceval_runtime_state {
-    struct {
-#ifdef PY_HAVE_PERF_TRAMPOLINE
-        perf_status_t status;
-        int perf_trampoline_type;
-        Py_ssize_t extra_code_index;
-        struct code_arena_st *code_arena;
-        struct trampoline_api_st trampoline_api;
-        FILE *map_file;
-        Py_ssize_t persist_after_fork;
-#else
-        int _not_used;
-#endif
-    } perf;
-    /* Pending calls to be made only on the main thread. */
-    // The signal machinery falls back on this
-    // so it must be especially stable and efficient.
-    // For example, we use a preallocated array
-    // for the list of pending calls.
-    struct _pending_calls pending_mainthread;
-    PyMutex sys_trace_profile_mutex;
-};
-
 
 #ifdef PY_HAVE_PERF_TRAMPOLINE
 # define _PyEval_RUNTIME_PERF_INIT \
@@ -116,18 +43,6 @@ struct _ceval_runtime_state {
 #endif
 
 
-struct _ceval_state {
-    /* This variable holds the global instrumentation version. When a thread is
-       running, this value is overlaid onto PyThreadState.eval_breaker so that
-       changes in the instrumentation version will trigger the eval breaker. */
-    uintptr_t instrumentation_version;
-    int recursion_limit;
-    struct _gil_runtime_state *gil;
-    int own_gil;
-    struct _pending_calls pending;
-};
-
-
 #ifdef __cplusplus
 }
 #endif
diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h
index c3edbb3dffe79b..530eaf80e08c63 100644
--- a/Include/internal/pycore_code.h
+++ b/Include/internal/pycore_code.h
@@ -8,30 +8,13 @@ extern "C" {
 #  error "this header requires Py_BUILD_CORE define"
 #endif
 
+#include "pycore_structs.h"     // _Py_CODEUNIT
 #include "pycore_stackref.h"    // _PyStackRef
 #include "pycore_lock.h"        // PyMutex
 #include "pycore_backoff.h"     // _Py_BackoffCounter
 #include "pycore_tstate.h"      // _PyThreadStateImpl
 
 
-/* Each instruction in a code object is a fixed-width value,
- * currently 2 bytes: 1-byte opcode + 1-byte oparg.  The EXTENDED_ARG
- * opcode allows for larger values but the current limit is 3 uses
- * of EXTENDED_ARG (see Python/compile.c), for a maximum
- * 32-bit value.  This aligns with the note in Python/compile.c
- * (compiler_addop_i_line) indicating that the max oparg value is
- * 2**32 - 1, rather than INT_MAX.
- */
-
-typedef union {
-    uint16_t cache;
-    struct {
-        uint8_t code;
-        uint8_t arg;
-    } op;
-    _Py_BackoffCounter counter;  // First cache entry of specializable op
-} _Py_CODEUNIT;
-
 #define _PyCode_CODE(CO) _Py_RVALUE((_Py_CODEUNIT *)(CO)->co_code_adaptive)
 #define _PyCode_NBYTES(CO) (Py_SIZE(CO) * (Py_ssize_t)sizeof(_Py_CODEUNIT))
 
@@ -67,16 +50,10 @@ _py_set_opcode(_Py_CODEUNIT *word, uint8_t opcode)
 #define _PyCode_HAS_INSTRUMENTATION(CODE) \
     (CODE->_co_instrumentation_version > 0)
 
-struct _py_code_state {
-    PyMutex mutex;
-    // Interned constants from code objects. Used by the free-threaded build.
-    struct _Py_hashtable_t *constants;
-};
 
 extern PyStatus _PyCode_Init(PyInterpreterState *interp);
 extern void _PyCode_Fini(PyInterpreterState *interp);
 
-#define CODE_MAX_WATCHERS 8
 
 /* PEP 659
  * Specialization and quickening structs and helper functions
@@ -185,14 +162,6 @@ typedef struct {
 
 #define INLINE_CACHE_ENTRIES_CONTAINS_OP CACHE_ENTRIES(_PyContainsOpCache)
 
-// Borrowed references to common callables:
-struct callable_cache {
-    PyObject *isinstance;
-    PyObject *len;
-    PyObject *list_append;
-    PyObject *object__getattribute__;
-};
-
 /* "Locals plus" for a code object is the set of locals + cell vars +
  * free vars.  This relates to variable names as well as offsets into
  * the "fast locals" storage array of execution frames.  The compiler
diff --git a/Include/internal/pycore_codecs.h b/Include/internal/pycore_codecs.h
index 4400be8b33dee7..7c44a449270202 100644
--- a/Include/internal/pycore_codecs.h
+++ b/Include/internal/pycore_codecs.h
@@ -9,6 +9,7 @@ extern "C" {
 #endif
 
 #include "pycore_lock.h"          // PyMutex
+#include "pycore_runtime_structs.h"    // struct codecs_state
 
 /* Initialize codecs-related state for the given interpreter, including
    registering the first codec search function. Must be called before any other
@@ -70,27 +71,6 @@ extern PyObject* _PyCodecInfo_GetIncrementalEncoder(
    PyObject *codec_info,
    const char *errors);
 
-// Per-interpreter state used by codecs.c.
-struct codecs_state {
-    // A list of callable objects used to search for codecs.
-    PyObject *search_path;
-
-    // A dict mapping codec names to codecs returned from a callable in
-    // search_path.
-    PyObject *search_cache;
-
-    // A dict mapping error handling strategies to functions to implement them.
-    PyObject *error_registry;
-
-#ifdef Py_GIL_DISABLED
-    // Used to safely delete a specific item from search_path.
-    PyMutex search_path_mutex;
-#endif
-
-    // Whether or not the rest of the state is initialized.
-    int initialized;
-};
-
 #ifdef __cplusplus
 }
 #endif
diff --git a/Include/internal/pycore_context.h 
b/Include/internal/pycore_context.h
index c2b98d15da68fa..c77ef7910c09aa 100644
--- a/Include/internal/pycore_context.h
+++ b/Include/internal/pycore_context.h
@@ -5,9 +5,7 @@
 #  error "this header requires Py_BUILD_CORE define"
 #endif
 
-#include "pycore_hamt.h"          // PyHamtObject
-
-#define CONTEXT_MAX_WATCHERS 8
+#include "pycore_structs.h"
 
 extern PyTypeObject _PyContextTokenMissing_Type;
 
diff --git a/Include/internal/pycore_dtoa.h b/Include/internal/pycore_dtoa.h
index e4222c5267d6be..2e2e990a7e2d38 100644
--- a/Include/internal/pycore_dtoa.h
+++ b/Include/internal/pycore_dtoa.h
@@ -11,53 +11,18 @@ extern "C" {
 #include "pycore_pymath.h"        // _PY_SHORT_FLOAT_REPR
 
 
-typedef uint32_t ULong;
-
-struct
-Bigint {
-    struct Bigint *next;
-    int k, maxwds, sign, wds;
-    ULong x[1];
-};
-
 #if defined(Py_USING_MEMORY_DEBUGGER) || _PY_SHORT_FLOAT_REPR == 0
 
-struct _dtoa_state {
-    int _not_used;
-};
 #define _dtoa_state_INIT(INTERP) \
     {0}
 
-#else  // !Py_USING_MEMORY_DEBUGGER && _PY_SHORT_FLOAT_REPR != 0
-
-/* The size of the Bigint freelist */
-#define Bigint_Kmax 7
+#else
 
-/* The size of the cached powers of 5 array */
-#define Bigint_Pow5size 8
-
-#ifndef PRIVATE_MEM
-#define PRIVATE_MEM 2304
-#endif
-#define Bigint_PREALLOC_SIZE \
-    ((PRIVATE_MEM+sizeof(double)-1)/sizeof(double))
-
-struct _dtoa_state {
-    // p5s is an array of powers of 5 of the form:
-    // 5**(2**(i+2)) for 0 <= i < Bigint_Pow5size
-    struct Bigint *p5s[Bigint_Pow5size];
-    // XXX This should be freed during runtime fini.
-    struct Bigint *freelist[Bigint_Kmax+1];
-    double preallocated[Bigint_PREALLOC_SIZE];
-    double *preallocated_next;
-};
 #define _dtoa_state_INIT(INTERP) \
     { \
         .preallocated_next = (INTERP)->dtoa.preallocated, \
     }
-
-#endif  // !Py_USING_MEMORY_DEBUGGER
-
+#endif
 
 extern double _Py_dg_strtod(const char *str, char **ptr);
 extern char* _Py_dg_dtoa(double d, int mode, int ndigits,
diff --git a/Include/internal/pycore_fileutils.h 
b/Include/internal/pycore_fileutils.h
index 13f86b01bbfe8f..1ef23588d85527 100644
--- a/Include/internal/pycore_fileutils.h
+++ b/Include/internal/pycore_fileutils.h
@@ -9,6 +9,7 @@ extern "C" {
 #endif
 
 #include <locale.h>               // struct lconv
+#include "pycore_runtime_structs.h" // _Py_error_handler
 
 
 /* A routine to check if a file descriptor can be select()-ed. */
@@ -19,22 +20,6 @@ extern "C" {
     #define _PyIsSelectable_fd(FD) ((unsigned int)(FD) < (unsigned 
int)FD_SETSIZE)
 #endif
 
-struct _fileutils_state {
-    int force_ascii;
-};
-
-typedef enum {
-    _Py_ERROR_UNKNOWN=0,
-    _Py_ERROR_STRICT,
-    _Py_ERROR_SURROGATEESCAPE,
-    _Py_ERROR_REPLACE,
-    _Py_ERROR_IGNORE,
-    _Py_ERROR_BACKSLASHREPLACE,
-    _Py_ERROR_SURROGATEPASS,
-    _Py_ERROR_XMLCHARREFREPLACE,
-    _Py_ERROR_OTHER
-} _Py_error_handler;
-
 // Export for '_testinternalcapi' shared extension
 PyAPI_FUNC(_Py_error_handler) _Py_GetErrorHandler(const char *errors);
 
diff --git a/Include/internal/pycore_floatobject.h 
b/Include/internal/pycore_floatobject.h
index f44b081b06cea5..317f984188bad8 100644
--- a/Include/internal/pycore_floatobject.h
+++ b/Include/internal/pycore_floatobject.h
@@ -17,20 +17,6 @@ extern PyStatus _PyFloat_InitTypes(PyInterpreterState *);
 extern void _PyFloat_FiniType(PyInterpreterState *);
 
 
-/* other API */
-
-enum _py_float_format_type {
-    _py_float_format_unknown,
-    _py_float_format_ieee_big_endian,
-    _py_float_format_ieee_little_endian,
-};
-
-struct _Py_float_runtime_state {
-    enum _py_float_format_type float_format;
-    enum _py_float_format_type double_format;
-};
-
-
 
 
 PyAPI_FUNC(void) _PyFloat_ExactDealloc(PyObject *op);
diff --git a/Include/internal/pycore_function.h 
b/Include/internal/pycore_function.h
index c45d281125febb..8fa040fec3bf03 100644
--- a/Include/internal/pycore_function.h
+++ b/Include/internal/pycore_function.h
@@ -16,33 +16,11 @@ extern PyObject* _PyFunction_Vectorcall(
     size_t nargsf,
     PyObject *kwnames);
 
-#define FUNC_MAX_WATCHERS 8
 
 #define FUNC_VERSION_UNSET 0
 #define FUNC_VERSION_CLEARED 1
 #define FUNC_VERSION_FIRST_VALID 2
 
-#define FUNC_VERSION_CACHE_SIZE (1<<12)  /* Must be a power of 2 */
-
-struct _func_version_cache_item {
-    PyFunctionObject *func;
-    PyObject *code;
-};
-
-struct _py_func_state {
-#ifdef Py_GIL_DISABLED
-    // Protects next_version
-    PyMutex mutex;
-#endif
-
-    uint32_t next_version;
-    // Borrowed references to function and code objects whose
-    // func_version % FUNC_VERSION_CACHE_SIZE
-    // once was equal to the index in the table.
-    // They are cleared when the function or code object is deallocated.
-    struct _func_version_cache_item 
func_version_cache[FUNC_VERSION_CACHE_SIZE];
-};
-
 extern PyFunctionObject* _PyFunction_FromConstructor(PyFrameConstructor 
*constr);
 
 static inline int
diff --git a/Include/internal/pycore_gc.h b/Include/internal/pycore_gc.h
index b1806df2706097..17125131183ed1 100644
--- a/Include/internal/pycore_gc.h
+++ b/Include/internal/pycore_gc.h
@@ -8,18 +8,7 @@ extern "C" {
 #  error "this header requires Py_BUILD_CORE define"
 #endif
 
-/* GC information is stored BEFORE the object structure. */
-typedef struct {
-    // Tagged pointer to next object in the list.
-    // 0 means the object is not tracked
-    uintptr_t _gc_next;
-
-    // Tagged pointer to previous object in the list.
-    // Lowest two bits are used for flags documented later.
-    uintptr_t _gc_prev;
-} PyGC_Head;
-
-#define _PyGC_Head_UNUSED PyGC_Head
+#include "pycore_runtime_structs.h"
 
 
 /* Get an object's GC head */
@@ -214,12 +203,6 @@ static inline void _PyGC_CLEAR_FINALIZED(PyObject *op) {
 #endif
 }
 
-
-/* GC runtime state */
-
-/* If we change this, we need to change the default value in the
-   signature of gc.collect. */
-#define NUM_GENERATIONS 3
 /*
    NOTE: about untracking of mutable objects.
 
@@ -261,90 +244,6 @@ static inline void _PyGC_CLEAR_FINALIZED(PyObject *op) {
    the algorithm was refined in response to issue #14775.
 */
 
-struct gc_generation {
-    PyGC_Head head;
-    int threshold; /* collection threshold */
-    int count; /* count of allocations or collections of younger
-                  generations */
-};
-
-struct gc_collection_stats {
-    /* number of collected objects */
-    Py_ssize_t collected;
-    /* total number of uncollectable objects (put into gc.garbage) */
-    Py_ssize_t uncollectable;
-};
-
-/* Running stats per generation */
-struct gc_generation_stats {
-    /* total number of collections */
-    Py_ssize_t collections;
-    /* total number of collected objects */
-    Py_ssize_t collected;
-    /* total number of uncollectable objects (put into gc.garbage) */
-    Py_ssize_t uncollectable;
-};
-
-enum _GCPhase {
-    GC_PHASE_MARK = 0,
-    GC_PHASE_COLLECT = 1
-};
-
-struct _gc_runtime_state {
-    /* List of objects that still need to be cleaned up, singly linked
-     * via their gc headers' gc_prev pointers.  */
-    PyObject *trash_delete_later;
-    /* Current call-stack depth of tp_dealloc calls. */
-    int trash_delete_nesting;
-
-    /* Is automatic collection enabled? */
-    int enabled;
-    int debug;
-    /* linked lists of container objects */
-    struct gc_generation young;
-    struct gc_generation old[2];
-    /* a permanent generation which won't be collected */
-    struct gc_generation permanent_generation;
-    struct gc_generation_stats generation_stats[NUM_GENERATIONS];
-    /* true if we are currently running the collector */
-    int collecting;
-    /* list of uncollectable objects */
-    PyObject *garbage;
-    /* a list of callbacks to be invoked when collection is performed */
-    PyObject *callbacks;
-
-    Py_ssize_t heap_size;
-    Py_ssize_t work_to_do;
-    /* Which of the old spaces is the visited space */
-    int visited_space;
-    int phase;
-
-#ifdef Py_GIL_DISABLED
-    /* This is the number of objects that survived the last full
-       collection. It approximates the number of long lived objects
-       tracked by the GC.
-
-       (by "full collection", we mean a collection of the oldest
-       generation). */
-    Py_ssize_t long_lived_total;
-    /* This is the number of objects that survived all "non-full"
-       collections, and are awaiting to undergo a full collection for
-       the first time. */
-    Py_ssize_t long_lived_pending;
-
-    /* True if gc.freeze() has been used. */
-    int freeze_active;
-#endif
-};
-
-#ifdef Py_GIL_DISABLED
-struct _gc_thread_state {
-    /* Thread-local allocation count. */
-    Py_ssize_t alloc_count;
-};
-#endif
-
-
 extern void _PyGC_InitState(struct _gc_runtime_state *);
 
 extern Py_ssize_t _PyGC_Collect(PyThreadState *tstate, int generation, 
_PyGC_Reason reason);
diff --git a/Include/internal/pycore_global_objects.h 
b/Include/internal/pycore_global_objects.h
index 611f01d1c32710..13251d4676be7d 100644
--- a/Include/internal/pycore_global_objects.h
+++ b/Include/internal/pycore_global_objects.h
@@ -8,18 +8,6 @@ extern "C" {
 #  error "this header requires Py_BUILD_CORE define"
 #endif
 
-#include "pycore_context.h"         // _PyContextTokenMissing
-#include "pycore_gc.h"              // _PyGC_Head_UNUSED
-#include "pycore_global_strings.h"  // struct _Py_global_strings
-#include "pycore_hamt.h"            // PyHamtNode_Bitmap
-#include "pycore_hashtable.h"       // _Py_hashtable_t
-#include "pycore_typeobject.h"      // pytype_slotdef
-
-
-// These would be in pycore_long.h if it weren't for an include cycle.
-#define _PY_NSMALLPOSINTS           257
-#define _PY_NSMALLNEGINTS           5
-
 
 // Only immutable objects should be considered runtime-global.
 // All others must be per-interpreter.
@@ -29,76 +17,16 @@ extern "C" {
 #define _Py_SINGLETON(NAME) \
     _Py_GLOBAL_OBJECT(singletons.NAME)
 
-struct _Py_cached_objects {
-    // XXX We could statically allocate the hashtable.
-    _Py_hashtable_t *interned_strings;
-};
-
-struct _Py_static_objects {
-    struct {
-        /* Small integers are preallocated in this array so that they
-         * can be shared.
-         * The integers that are preallocated are those in the range
-         * -_PY_NSMALLNEGINTS (inclusive) to _PY_NSMALLPOSINTS (exclusive).
-         */
-        PyLongObject small_ints[_PY_NSMALLNEGINTS + _PY_NSMALLPOSINTS];
-
-        PyBytesObject bytes_empty;
-        struct {
-            PyBytesObject ob;
-            char eos;
-        } bytes_characters[256];
-
-        struct _Py_global_strings strings;
-
-        _PyGC_Head_UNUSED _tuple_empty_gc_not_used;
-        PyTupleObject tuple_empty;
-
-        _PyGC_Head_UNUSED _hamt_bitmap_node_empty_gc_not_used;
-        PyHamtNode_Bitmap hamt_bitmap_node_empty;
-        _PyContextTokenMissing context_token_missing;
-    } singletons;
-};
 
 #define _Py_INTERP_CACHED_OBJECT(interp, NAME) \
     (interp)->cached_objects.NAME
 
-struct _Py_interp_cached_objects {
-#ifdef Py_GIL_DISABLED
-    PyMutex interned_mutex;
-#endif
-    PyObject *interned_strings;
-
-    /* object.__reduce__ */
-    PyObject *objreduce;
-    PyObject *type_slots_pname;
-    pytype_slotdef *type_slots_ptrs[MAX_EQUIV];
-
-    /* TypeVar and related types */
-    PyTypeObject *generic_type;
-    PyTypeObject *typevar_type;
-    PyTypeObject *typevartuple_type;
-    PyTypeObject *paramspec_type;
-    PyTypeObject *paramspecargs_type;
-    PyTypeObject *paramspeckwargs_type;
-    PyTypeObject *constevaluator_type;
-};
 
 #define _Py_INTERP_STATIC_OBJECT(interp, NAME) \
     (interp)->static_objects.NAME
 #define _Py_INTERP_SINGLETON(interp, NAME) \
     _Py_INTERP_STATIC_OBJECT(interp, singletons.NAME)
 
-struct _Py_interp_static_objects {
-    struct {
-        int _not_used;
-        // hamt_empty is here instead of global because of its weakreflist.
-        _PyGC_Head_UNUSED _hamt_empty_gc_not_used;
-        PyHamtObject hamt_empty;
-        PyBaseExceptionObject last_resort_memory_error;
-    } singletons;
-};
-
 
 #ifdef __cplusplus
 }
diff --git a/Include/internal/pycore_global_strings.h 
b/Include/internal/pycore_global_strings.h
index 97a75d0c46c867..5056128dc97ca0 100644
--- a/Include/internal/pycore_global_strings.h
+++ b/Include/internal/pycore_global_strings.h
@@ -8,6 +8,8 @@ extern "C" {
 #  error "this header requires Py_BUILD_CORE define"
 #endif
 
+#include "pycore_global_objects.h"  // struct _Py_SINGLETON
+
 // The data structure & init here are inspired by Tools/build/deepfreeze.py.
 
 // All field names generated by ASCII_STR() have a common prefix,
diff --git a/Include/internal/pycore_hamt.h b/Include/internal/pycore_hamt.h
index d8742c7cb63578..f973ce6dcb20e0 100644
--- a/Include/internal/pycore_hamt.h
+++ b/Include/internal/pycore_hamt.h
@@ -5,6 +5,7 @@
 #  error "this header requires Py_BUILD_CORE define"
 #endif
 
+#include "pycore_structs.h"       // PyHamtNode
 
 /*
 HAMT tree is shaped by hashes of keys. Every group of 5 bits of a hash denotes
@@ -34,28 +35,6 @@ extern PyTypeObject _PyHamtItems_Type;
 #define PyHamt_Check(o) Py_IS_TYPE((o), &_PyHamt_Type)
 
 
-/* Abstract tree node. */
-typedef struct {
-    PyObject_HEAD
-} PyHamtNode;
-
-
-/* An HAMT immutable mapping collection. */
-typedef struct {
-    PyObject_HEAD
-    PyHamtNode *h_root;
-    PyObject *h_weakreflist;
-    Py_ssize_t h_count;
-} PyHamtObject;
-
-
-typedef struct {
-    PyObject_VAR_HEAD
-    uint32_t b_bitmap;
-    PyObject *b_array[1];
-} PyHamtNode_Bitmap;
-
-
 /* A struct to hold the state of depth-first traverse of the tree.
 
    HAMT is an immutable collection.  Iterators will hold a strong reference
diff --git a/Include/internal/pycore_import.h b/Include/internal/pycore_import.h
index 5fe60df0a92fbc..2b68228f8c6470 100644
--- a/Include/internal/pycore_import.h
+++ b/Include/internal/pycore_import.h
@@ -10,6 +10,7 @@ extern "C" {
 #endif
 
 #include "pycore_lock.h"          // PyMutex
+#include "pycore_runtime_structs.h"  // _import_state
 #include "pycore_hashtable.h"     // _Py_hashtable_t
 
 extern int _PyImport_IsInitialized(PyInterpreterState *);
@@ -31,73 +32,6 @@ extern int _PyImport_FixupBuiltin(
     PyObject *modules
     );
 
-
-struct _import_runtime_state {
-    /* The builtin modules (defined in config.c). */
-    struct _inittab *inittab;
-    /* The most recent value assigned to a PyModuleDef.m_base.m_index.
-       This is incremented each time PyModuleDef_Init() is called,
-       which is just about every time an extension module is imported.
-       See PyInterpreterState.modules_by_index for more info. */
-    Py_ssize_t last_module_index;
-    struct {
-        /* A lock to guard the cache. */
-        PyMutex mutex;
-        /* The actual cache of (filename, name, PyModuleDef) for modules.
-           Only legacy (single-phase init) extension modules are added
-           and only if they support multiple initialization (m_size >= 0)
-           or are imported in the main interpreter.
-           This is initialized lazily in fix_up_extension() in import.c.
-           Modules are added there and looked up in _imp.find_extension(). */
-        _Py_hashtable_t *hashtable;
-    } extensions;
-    /* Package context -- the full module name for package imports */
-    const char * pkgcontext;
-};
-
-struct _import_state {
-    /* cached sys.modules dictionary */
-    PyObject *modules;
-    /* This is the list of module objects for all legacy (single-phase init)
-       extension modules ever loaded in this process (i.e. imported
-       in this interpreter or in any other).  Py_None stands in for
-       modules that haven't actually been imported in this interpreter.
-
-       A module's index (PyModuleDef.m_base.m_index) is used to look up
-       the corresponding module object for this interpreter, if any.
-       (See PyState_FindModule().)  When any extension module
-       is initialized during import, its moduledef gets initialized by
-       PyModuleDef_Init(), and the first time that happens for each
-       PyModuleDef, its index gets set to the current value of
-       a global counter (see _PyRuntimeState.imports.last_module_index).
-       The entry for that index in this interpreter remains unset until
-       the module is actually imported here.  (Py_None is used as
-       a placeholder.)  Note that multi-phase init modules always get
-       an index for which there will never be a module set.
-
-       This is initialized lazily in PyState_AddModule(), which is also
-       where modules get added. */
-    PyObject *modules_by_index;
-    /* importlib module._bootstrap */
-    PyObject *importlib;
-    /* override for config->use_frozen_modules (for tests)
-       (-1: "off", 1: "on", 0: no override) */
-    int override_frozen_modules;
-    int override_multi_interp_extensions_check;
-#ifdef HAVE_DLOPEN
-    int dlopenflags;
-#endif
-    PyObject *import_func;
-    /* The global import lock. */
-    _PyRecursiveMutex lock;
-    /* diagnostic info in PyImport_ImportModuleLevelObject() */
-    struct {
-        int import_level;
-        PyTime_t accumulated;
-        int header;
-    } find_and_load;
-};
-
 #ifdef HAVE_DLOPEN
 #  include <dlfcn.h>              // RTLD_NOW, RTLD_LAZY
 #  if HAVE_DECL_RTLD_NOW
diff --git a/Include/internal/pycore_index_pool.h 
b/Include/internal/pycore_index_pool.h
index e81bfd4d6ed03d..2ce06be48c5379 100644
--- a/Include/internal/pycore_index_pool.h
+++ b/Include/internal/pycore_index_pool.h
@@ -13,32 +13,12 @@ extern "C" {
 
 #ifdef Py_GIL_DISABLED
 
+#include "pycore_interp_structs.h"
+
 // This contains code for allocating unique indices in an array. It is used by
 // the free-threaded build to assign each thread a globally unique index into
 // each code object's thread-local bytecode array.
 
-// A min-heap of indices
-typedef struct _PyIndexHeap {
-    int32_t *values;
-
-    // Number of items stored in values
-    Py_ssize_t size;
-
-    // Maximum number of items that can be stored in values
-    Py_ssize_t capacity;
-} _PyIndexHeap;
-
-// An unbounded pool of indices. Indices are allocated starting from 0. They
-// may be released back to the pool once they are no longer in use.
-typedef struct _PyIndexPool {
-    PyMutex mutex;
-
-    // Min heap of indices available for allocation
-    _PyIndexHeap free_indices;
-
-    // Next index to allocate if no free indices are available
-    int32_t next_index;
-} _PyIndexPool;
 
 // Allocate the smallest available index. Returns -1 on error.
 extern int32_t _PyIndexPool_AllocIndex(_PyIndexPool *indices);
diff --git a/Include/internal/pycore_instruments.h 
b/Include/internal/pycore_instruments.h
index 92d8f056f402fc..0e85f97073dd67 100644
--- a/Include/internal/pycore_instruments.h
+++ b/Include/internal/pycore_instruments.h
@@ -5,14 +5,12 @@
 #  error "this header requires Py_BUILD_CORE define"
 #endif
 
-#include "pycore_frame.h"         // _PyInterpreterFrame
+#include "pycore_structs.h"       // _Py_CODEUNIT
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-#define PY_MONITORING_TOOL_IDS 8
-
 typedef uint32_t _PyMonitoringEventSet;
 
 /* Tool IDs */
@@ -36,32 +34,32 @@ int _PyMonitoring_GetLocalEvents(PyCodeObject *code, int 
tool_id, _PyMonitoringE
 
 extern int
 _Py_call_instrumentation(PyThreadState *tstate, int event,
-    _PyInterpreterFrame *frame, _Py_CODEUNIT *instr);
+    struct _PyInterpreterFrame *frame, _Py_CODEUNIT *instr);
 
 extern int
-_Py_call_instrumentation_line(PyThreadState *tstate, _PyInterpreterFrame* 
frame,
+_Py_call_instrumentation_line(PyThreadState *tstate, struct 
_PyInterpreterFrame* frame,
                               _Py_CODEUNIT *instr, _Py_CODEUNIT *prev);
 
 extern int
 _Py_call_instrumentation_instruction(
-    PyThreadState *tstate, _PyInterpreterFrame* frame, _Py_CODEUNIT *instr);
+    PyThreadState *tstate, struct _PyInterpreterFrame* frame, _Py_CODEUNIT 
*instr);
 
 _Py_CODEUNIT *
 _Py_call_instrumentation_jump(
     _Py_CODEUNIT *instr, PyThreadState *tstate, int event,
-    _PyInterpreterFrame *frame, _Py_CODEUNIT *src, _Py_CODEUNIT *dest);
+    struct _PyInterpreterFrame *frame, _Py_CODEUNIT *src, _Py_CODEUNIT *dest);
 
 extern int
 _Py_call_instrumentation_arg(PyThreadState *tstate, int event,
-    _PyInterpreterFrame *frame, _Py_CODEUNIT *instr, PyObject *arg);
+    struct _PyInterpreterFrame *frame, _Py_CODEUNIT *instr, PyObject *arg);
 
 extern int
 _Py_call_instrumentation_2args(PyThreadState *tstate, int event,
-    _PyInterpreterFrame *frame, _Py_CODEUNIT *instr, PyObject *arg0, PyObject 
*arg1);
+    struct _PyInterpreterFrame *frame, _Py_CODEUNIT *instr, PyObject *arg0, 
PyObject *arg1);
 
 extern void
 _Py_call_instrumentation_exc2(PyThreadState *tstate, int event,
-    _PyInterpreterFrame *frame, _Py_CODEUNIT *instr, PyObject *arg0, PyObject 
*arg1);
+    struct _PyInterpreterFrame *frame, _Py_CODEUNIT *instr, PyObject *arg0, 
PyObject *arg1);
 
 extern int
 _Py_Instrumentation_GetLine(PyCodeObject *code, int index);
@@ -69,6 +67,59 @@ _Py_Instrumentation_GetLine(PyCodeObject *code, int index);
 extern PyObject _PyInstrumentation_MISSING;
 extern PyObject _PyInstrumentation_DISABLE;
 
+
+/* Total tool ids available */
+#define  PY_MONITORING_TOOL_IDS 8
+/* Count of all local monitoring events */
+#define  _PY_MONITORING_LOCAL_EVENTS 11
+/* Count of all "real" monitoring events (not derived from other events) */
+#define _PY_MONITORING_UNGROUPED_EVENTS 16
+/* Count of all  monitoring events */
+#define _PY_MONITORING_EVENTS 19
+
+/* Tables of which tools are active for each monitored event. */
+typedef struct _Py_LocalMonitors {
+    uint8_t tools[_PY_MONITORING_LOCAL_EVENTS];
+} _Py_LocalMonitors;
+
+typedef struct _Py_GlobalMonitors {
+    uint8_t tools[_PY_MONITORING_UNGROUPED_EVENTS];
+} _Py_GlobalMonitors;
+
+/* Ancillary data structure used for instrumentation.
+   Line instrumentation creates this with sufficient
+   space for one entry per code unit. The total size
+   of the data will be `bytes_per_entry * Py_SIZE(code)` */
+typedef struct {
+    uint8_t bytes_per_entry;
+    uint8_t data[1];
+} _PyCoLineInstrumentationData;
+
+
+/* Main data structure used for instrumentation.
+ * This is allocated when needed for instrumentation
+ */
+typedef struct _PyCoMonitoringData {
+    /* Monitoring specific to this code object */
+    _Py_LocalMonitors local_monitors;
+    /* Monitoring that is active on this code object */
+    _Py_LocalMonitors active_monitors;
+    /* The tools that are to be notified for events for the matching code unit 
*/
+    uint8_t *tools;
+    /* The version of tools when they instrument the code */
+    uintptr_t tool_versions[PY_MONITORING_TOOL_IDS];
+    /* Information to support line events */
+    _PyCoLineInstrumentationData *lines;
+    /* The tools that are to be notified for line events for the matching code 
unit */
+    uint8_t *line_tools;
+    /* Information to support instruction events */
+    /* The underlying instructions, which can themselves be instrumented */
+    uint8_t *per_instruction_opcodes;
+    /* The tools that are to be notified for instruction events for the 
matching code unit */
+    uint8_t *per_instruction_tools;
+} _PyCoMonitoringData;
+
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h
index c247c7270d1caa..791038d45b3775 100644
--- a/Include/internal/pycore_interp.h
+++ b/Include/internal/pycore_interp.h
@@ -10,8 +10,7 @@ extern "C" {
 
 #include <stdbool.h>              // bool
 
-#include "pycore_ast_state.h"     // struct ast_state
-#include "pycore_atexit.h"        // struct atexit_state
+#include "pycore_runtime_structs.h"
 #include "pycore_ceval_state.h"   // struct _ceval_state
 #include "pycore_code.h"          // struct callable_cache
 #include "pycore_codecs.h"        // struct codecs_state
@@ -43,70 +42,9 @@ extern "C" {
 #include "pycore_warnings.h"      // struct _warnings_runtime_state
 
 
-struct _Py_long_state {
-    int max_str_digits;
-};
-
-// Support for stop-the-world events. This exists in both the PyRuntime struct
-// for global pauses and in each PyInterpreterState for per-interpreter pauses.
-struct _stoptheworld_state {
-    PyMutex mutex;       // Serializes stop-the-world attempts.
-
-    // NOTE: The below fields are protected by HEAD_LOCK(runtime), not by the
-    // above mutex.
-    bool requested;      // Set when a pause is requested.
-    bool world_stopped;  // Set when the world is stopped.
-    bool is_global;      // Set when contained in PyRuntime struct.
-
-    PyEvent stop_event;  // Set when thread_countdown reaches zero.
-    Py_ssize_t thread_countdown;  // Number of threads that must pause.
-
-    PyThreadState *requester; // Thread that requested the pause (may be NULL).
-};
-
-#ifdef Py_GIL_DISABLED
-// This should be prime but otherwise the choice is arbitrary. A larger value
-// increases concurrency at the expense of memory.
-#  define NUM_WEAKREF_LIST_LOCKS 127
-#endif
-
-/* cross-interpreter data registry */
-
-/* Tracks some rare events per-interpreter, used by the optimizer to turn 
on/off
-   specific optimizations. */
-typedef struct _rare_events {
-    /* Setting an object's class, obj.__class__ = ... */
-    uint8_t set_class;
-    /* Setting the bases of a class, cls.__bases__ = ... */
-    uint8_t set_bases;
-    /* Setting the PEP 523 frame eval function, 
_PyInterpreterState_SetFrameEvalFunc() */
-    uint8_t set_eval_frame_func;
-    /* Modifying the builtins,  __builtins__.__dict__[var] = ... */
-    uint8_t builtin_dict;
-    /* Modifying a function, e.g. func.__defaults__ = ..., etc. */
-    uint8_t func_modification;
-} _rare_events;
 
 /* interpreter state */
 
-/* PyInterpreterState holds the global state for one of the runtime's
-   interpreters.  Typically the initial (main) interpreter is the only one.
-
-   The PyInterpreterState typedef is in Include/pytypedefs.h.
-   */
-struct _is {
-
-    /* This struct contains the eval_breaker,
-     * which is by far the hottest field in this struct
-     * and should be placed at the beginning. */
-    struct _ceval_state ceval;
-
-    PyInterpreterState *next;
-
-    int64_t id;
-    Py_ssize_t id_refcount;
-    int requires_idref;
-
 #define _PyInterpreterState_WHENCE_NOTSET -1
 #define _PyInterpreterState_WHENCE_UNKNOWN 0
 #define _PyInterpreterState_WHENCE_RUNTIME 1
@@ -115,193 +53,6 @@ struct _is {
 #define _PyInterpreterState_WHENCE_XI 4
 #define _PyInterpreterState_WHENCE_STDLIB 5
 #define _PyInterpreterState_WHENCE_MAX 5
-    long _whence;
-
-    /* Has been initialized to a safe state.
-
-       In order to be effective, this must be set to 0 during or right
-       after allocation. */
-    int _initialized;
-    /* Has been fully initialized via pylifecycle.c. */
-    int _ready;
-    int finalizing;
-
-    uintptr_t last_restart_version;
-    struct pythreads {
-        uint64_t next_unique_id;
-        /* The linked list of threads, newest first. */
-        PyThreadState *head;
-        _PyThreadStateImpl *preallocated;
-        /* The thread currently executing in the __main__ module, if any. */
-        PyThreadState *main;
-        /* Used in Modules/_threadmodule.c. */
-        Py_ssize_t count;
-        /* Support for runtime thread stack size tuning.
-           A value of 0 means using the platform's default stack size
-           or the size specified by the THREAD_STACK_SIZE macro. */
-        /* Used in Python/thread.c. */
-        size_t stacksize;
-    } threads;
-
-    /* Reference to the _PyRuntime global variable. This field exists
-       to not have to pass runtime in addition to tstate to a function.
-       Get runtime from tstate: tstate->interp->runtime. */
-    struct pyruntimestate *runtime;
-
-    /* Set by Py_EndInterpreter().
-
-       Use _PyInterpreterState_GetFinalizing()
-       and _PyInterpreterState_SetFinalizing()
-       to access it, don't access it directly. */
-    PyThreadState* _finalizing;
-    /* The ID of the OS thread in which we are finalizing. */
-    unsigned long _finalizing_id;
-
-    struct _gc_runtime_state gc;
-
-    /* The following fields are here to avoid allocation during init.
-       The data is exposed through PyInterpreterState pointer fields.
-       These fields should not be accessed directly outside of init.
-
-       All other PyInterpreterState pointer fields are populated when
-       needed and default to NULL.
-
-       For now there are some exceptions to that rule, which require
-       allocation during init.  These will be addressed on a case-by-case
-       basis.  Also see _PyRuntimeState regarding the various mutex fields.
-       */
-
-    // Dictionary of the sys module
-    PyObject *sysdict;
-
-    // Dictionary of the builtins module
-    PyObject *builtins;
-
-    struct _import_state imports;
-
-    /* The per-interpreter GIL, which might not be used. */
-    struct _gil_runtime_state _gil;
-
-     /* ---------- IMPORTANT ---------------------------
-     The fields above this line are declared as early as
-     possible to facilitate out-of-process observability
-     tools. */
-
-    struct codecs_state codecs;
-
-    PyConfig config;
-    unsigned long feature_flags;
-
-    PyObject *dict;  /* Stores per-interpreter state */
-
-    PyObject *sysdict_copy;
-    PyObject *builtins_copy;
-    // Initialized to _PyEval_EvalFrameDefault().
-    _PyFrameEvalFunction eval_frame;
-
-    PyFunction_WatchCallback func_watchers[FUNC_MAX_WATCHERS];
-    // One bit is set for each non-NULL entry in func_watchers
-    uint8_t active_func_watchers;
-
-    Py_ssize_t co_extra_user_count;
-    freefunc co_extra_freefuncs[MAX_CO_EXTRA_USERS];
-
-    /* cross-interpreter data and utils */
-    _PyXI_state_t xi;
-
-#ifdef HAVE_FORK
-    PyObject *before_forkers;
-    PyObject *after_forkers_parent;
-    PyObject *after_forkers_child;
-#endif
-
-    struct _warnings_runtime_state warnings;
-    struct atexit_state atexit;
-    struct _stoptheworld_state stoptheworld;
-    struct _qsbr_shared qsbr;
-
-#if defined(Py_GIL_DISABLED)
-    struct _mimalloc_interp_state mimalloc;
-    struct _brc_state brc;  // biased reference counting state
-    struct _Py_unique_id_pool unique_ids;  // object ids for per-thread 
refcounts
-    PyMutex weakref_locks[NUM_WEAKREF_LIST_LOCKS];
-    _PyIndexPool tlbc_indices;
-#endif
-    // Per-interpreter list of tasks, any lingering tasks from thread
-    // states gets added here and removed from the corresponding
-    // thread state's list.
-    struct llist_node asyncio_tasks_head;
-    // `asyncio_tasks_lock` is used when tasks are moved
-    // from thread's list to interpreter's list.
-    PyMutex asyncio_tasks_lock;
-
-    // Per-interpreter state for the obmalloc allocator.  For the main
-    // interpreter and for all interpreters that don't have their
-    // own obmalloc state, this points to the static structure in
-    // obmalloc.c obmalloc_state_main.  For other interpreters, it is
-    // heap allocated by _PyMem_init_obmalloc() and freed when the
-    // interpreter structure is freed.  In the case of a heap allocated
-    // obmalloc state, it is not safe to hold on to or use memory after
-    // the interpreter is freed. The obmalloc state corresponding to
-    // that allocated memory is gone.  See free_obmalloc_arenas() for
-    // more comments.
-    struct _obmalloc_state *obmalloc;
-
-    PyObject *audit_hooks;
-    PyType_WatchCallback type_watchers[TYPE_MAX_WATCHERS];
-    PyCode_WatchCallback code_watchers[CODE_MAX_WATCHERS];
-    PyContext_WatchCallback context_watchers[CONTEXT_MAX_WATCHERS];
-    // One bit is set for each non-NULL entry in code_watchers
-    uint8_t active_code_watchers;
-    uint8_t active_context_watchers;
-
-    struct _py_object_state object_state;
-    struct _Py_unicode_state unicode;
-    struct _Py_long_state long_state;
-    struct _dtoa_state dtoa;
-    struct _py_func_state func_state;
-    struct _py_code_state code_state;
-
-    struct _Py_dict_state dict_state;
-    struct _Py_exc_state exc_state;
-    struct _Py_mem_interp_free_queue mem_free_queue;
-
-    struct ast_state ast;
-    struct types_state types;
-    struct callable_cache callable_cache;
-    bool jit;
-    _PyExecutorObject *executor_list_head;
-    size_t trace_run_counter;
-    _rare_events rare_events;
-    PyDict_WatchCallback builtins_dict_watcher;
-
-    _Py_GlobalMonitors monitors;
-    bool sys_profile_initialized;
-    bool sys_trace_initialized;
-    Py_ssize_t sys_profiling_threads; /* Count of threads with c_profilefunc 
set */
-    Py_ssize_t sys_tracing_threads; /* Count of threads with c_tracefunc set */
-    PyObject 
*monitoring_callables[PY_MONITORING_TOOL_IDS][_PY_MONITORING_EVENTS];
-    PyObject *monitoring_tool_names[PY_MONITORING_TOOL_IDS];
-    uintptr_t monitoring_tool_versions[PY_MONITORING_TOOL_IDS];
-
-    struct _Py_interp_cached_objects cached_objects;
-    struct _Py_interp_static_objects static_objects;
-
-    Py_ssize_t _interactive_src_count;
-
-    /* the initial PyInterpreterState.threads.head */
-    _PyThreadStateImpl _initial_thread;
-    // _initial_thread should be the last field of PyInterpreterState.
-    // See https://github.com/python/cpython/issues/127117.
-
-#if !defined(Py_GIL_DISABLED) && defined(Py_STACKREF_DEBUG)
-    uint64_t next_stackref;
-    _Py_hashtable_t *open_stackrefs_table;
-#  ifdef Py_STACKREF_CLOSE_DEBUG
-    _Py_hashtable_t *closed_stackrefs_table;
-#  endif
-#endif
-};
 
 
 /* other API */
diff --git a/Include/internal/pycore_interp_structs.h 
b/Include/internal/pycore_interp_structs.h
new file mode 100644
index 00000000000000..ec788de316fcef
--- /dev/null
+++ b/Include/internal/pycore_interp_structs.h
@@ -0,0 +1,967 @@
+#ifndef Py_INTERNAL_INTERP_STRUCTS_H
+#define Py_INTERNAL_INTERP_STRUCTS_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "pycore_structs.h"
+#include "pycore_pymath.h" // _PY_SHORT_FLOAT_REPR
+#include "pycore_llist.h"
+#include "pycore_ast_state.h"     // struct ast_state
+
+
+/* This file contains the struct definitions for interpreter state
+ * and other necessary structs */
+
+#define CODE_MAX_WATCHERS 8
+#define CONTEXT_MAX_WATCHERS 8
+#define FUNC_MAX_WATCHERS 8
+#define TYPE_MAX_WATCHERS 8
+
+
+#ifdef Py_GIL_DISABLED
+// This should be prime but otherwise the choice is arbitrary. A larger value
+// increases concurrency at the expense of memory.
+#  define NUM_WEAKREF_LIST_LOCKS 127
+#endif
+
+typedef int (*_Py_pending_call_func)(void *);
+
+struct _pending_call {
+    _Py_pending_call_func func;
+    void *arg;
+    int flags;
+};
+
+#define PENDINGCALLSARRAYSIZE 300
+
+struct _pending_calls {
+    PyThreadState *handling_thread;
+    PyMutex mutex;
+    /* Request for running pending calls. */
+    int32_t npending;
+    /* The maximum allowed number of pending calls.
+       If the queue fills up to this point then _PyEval_AddPendingCall()
+       will return _Py_ADD_PENDING_FULL. */
+    int32_t max;
+    /* We don't want a flood of pending calls to interrupt any one thread
+       for too long, so we keep a limit on the number handled per pass.
+       A value of 0 means there is no limit (other than the maximum
+       size of the list of pending calls). */
+    int32_t maxloop;
+    struct _pending_call calls[PENDINGCALLSARRAYSIZE];
+    int first;
+    int next;
+};
+
+typedef enum {
+    PERF_STATUS_FAILED = -1,  // Perf trampoline is in an invalid state
+    PERF_STATUS_NO_INIT = 0,  // Perf trampoline is not initialized
+    PERF_STATUS_OK = 1,       // Perf trampoline is ready to be executed
+} perf_status_t;
+
+#ifdef PY_HAVE_PERF_TRAMPOLINE
+struct code_arena_st;
+
+struct trampoline_api_st {
+    void* (*init_state)(void);
+    void (*write_state)(void* state, const void *code_addr,
+                        unsigned int code_size, PyCodeObject* code);
+    int (*free_state)(void* state);
+    void *state;
+    Py_ssize_t code_padding;
+};
+#endif
+
+
+struct _ceval_runtime_state {
+    struct {
+#ifdef PY_HAVE_PERF_TRAMPOLINE
+        perf_status_t status;
+        int perf_trampoline_type;
+        Py_ssize_t extra_code_index;
+        struct code_arena_st *code_arena;
+        struct trampoline_api_st trampoline_api;
+        FILE *map_file;
+        Py_ssize_t persist_after_fork;
+#else
+        int _not_used;
+#endif
+    } perf;
+    /* Pending calls to be made only on the main thread. */
+    // The signal machinery falls back on this
+    // so it must be especially stable and efficient.
+    // For example, we use a preallocated array
+    // for the list of pending calls.
+    struct _pending_calls pending_mainthread;
+    PyMutex sys_trace_profile_mutex;
+};
+
+
+struct _ceval_state {
+    /* This variable holds the global instrumentation version. When a thread is
+       running, this value is overlaid onto PyThreadState.eval_breaker so that
+       changes in the instrumentation version will trigger the eval breaker. */
+    uintptr_t instrumentation_version;
+    int recursion_limit;
+    struct _gil_runtime_state *gil;
+    int own_gil;
+    struct _pending_calls pending;
+};
+
+
+//###############
+// runtime atexit
+
+typedef void (*atexit_callbackfunc)(void);
+
+struct _atexit_runtime_state {
+    PyMutex mutex;
+#define NEXITFUNCS 32
+    atexit_callbackfunc callbacks[NEXITFUNCS];
+    int ncallbacks;
+};
+
+
+//###################
+// interpreter atexit
+
+typedef void (*atexit_datacallbackfunc)(void *);
+
+typedef struct atexit_callback {
+    atexit_datacallbackfunc func;
+    void *data;
+    struct atexit_callback *next;
+} atexit_callback;
+
+struct atexit_state {
+#ifdef Py_GIL_DISABLED
+    PyMutex ll_callbacks_lock;
+#endif
+    atexit_callback *ll_callbacks;
+
+    // XXX The rest of the state could be moved to the atexit module state
+    // and a low-level callback added for it during module exec.
+    // For the moment we leave it here.
+
+    // List containing tuples with callback information.
+    // e.g. [(func, args, kwargs), ...]
+    PyObject *callbacks;
+};
+
+
+/****** Garbage collector **********/
+
+/* GC information is stored BEFORE the object structure. */
+typedef struct {
+    // Tagged pointer to next object in the list.
+    // 0 means the object is not tracked
+    uintptr_t _gc_next;
+
+    // Tagged pointer to previous object in the list.
+    // Lowest two bits are used for flags documented later.
+    uintptr_t _gc_prev;
+} PyGC_Head;
+
+#define _PyGC_Head_UNUSED PyGC_Head
+
+struct gc_generation {
+    PyGC_Head head;
+    int threshold; /* collection threshold */
+    int count; /* count of allocations or collections of younger
+                  generations */
+};
+
+struct gc_collection_stats {
+    /* number of collected objects */
+    Py_ssize_t collected;
+    /* total number of uncollectable objects (put into gc.garbage) */
+    Py_ssize_t uncollectable;
+};
+
+/* Running stats per generation */
+struct gc_generation_stats {
+    /* total number of collections */
+    Py_ssize_t collections;
+    /* total number of collected objects */
+    Py_ssize_t collected;
+    /* total number of uncollectable objects (put into gc.garbage) */
+    Py_ssize_t uncollectable;
+};
+
+enum _GCPhase {
+    GC_PHASE_MARK = 0,
+    GC_PHASE_COLLECT = 1
+};
+
+/* If we change this, we need to change the default value in the
+   signature of gc.collect. */
+#define NUM_GENERATIONS 3
+
+struct _gc_runtime_state {
+    /* List of objects that still need to be cleaned up, singly linked
+     * via their gc headers' gc_prev pointers.  */
+    PyObject *trash_delete_later;
+    /* Current call-stack depth of tp_dealloc calls. */
+    int trash_delete_nesting;
+
+    /* Is automatic collection enabled? */
+    int enabled;
+    int debug;
+    /* linked lists of container objects */
+    struct gc_generation young;
+    struct gc_generation old[2];
+    /* a permanent generation which won't be collected */
+    struct gc_generation permanent_generation;
+    struct gc_generation_stats generation_stats[NUM_GENERATIONS];
+    /* true if we are currently running the collector */
+    int collecting;
+    /* list of uncollectable objects */
+    PyObject *garbage;
+    /* a list of callbacks to be invoked when collection is performed */
+    PyObject *callbacks;
+
+    Py_ssize_t heap_size;
+    Py_ssize_t work_to_do;
+    /* Which of the old spaces is the visited space */
+    int visited_space;
+    int phase;
+
+#ifdef Py_GIL_DISABLED
+    /* This is the number of objects that survived the last full
+       collection. It approximates the number of long lived objects
+       tracked by the GC.
+
+       (by "full collection", we mean a collection of the oldest
+       generation). */
+    Py_ssize_t long_lived_total;
+    /* This is the number of objects that survived all "non-full"
+       collections, and are awaiting to undergo a full collection for
+       the first time. */
+    Py_ssize_t long_lived_pending;
+
+    /* True if gc.freeze() has been used. */
+    int freeze_active;
+#endif
+};
+
+#ifdef Py_GIL_DISABLED
+struct _gc_thread_state {
+    /* Thread-local allocation count. */
+    Py_ssize_t alloc_count;
+};
+#endif
+
+#include "pycore_gil.h"
+
+/****** Thread state **************/
+#include "pytypedefs.h"
+#include "pystate.h"
+#include "pycore_tstate.h"
+
+
+/**** Import ********/
+
+struct _import_runtime_state {
+    /* The builtin modules (defined in config.c). */
+    struct _inittab *inittab;
+    /* The most recent value assigned to a PyModuleDef.m_base.m_index.
+       This is incremented each time PyModuleDef_Init() is called,
+       which is just about every time an extension module is imported.
+       See PyInterpreterState.modules_by_index for more info. */
+    Py_ssize_t last_module_index;
+    struct {
+        /* A lock to guard the cache. */
+        PyMutex mutex;
+        /* The actual cache of (filename, name, PyModuleDef) for modules.
+           Only legacy (single-phase init) extension modules are added
+           and only if they support multiple initialization (m_size >= 0)
+           or are imported in the main interpreter.
+           This is initialized lazily in fix_up_extension() in import.c.
+           Modules are added there and looked up in _imp.find_extension(). */
+        struct _Py_hashtable_t *hashtable;
+    } extensions;
+    /* Package context -- the full module name for package imports */
+    const char * pkgcontext;
+};
+
+struct _import_state {
+    /* cached sys.modules dictionary */
+    PyObject *modules;
+    /* This is the list of module objects for all legacy (single-phase init)
+       extension modules ever loaded in this process (i.e. imported
+       in this interpreter or in any other).  Py_None stands in for
+       modules that haven't actually been imported in this interpreter.
+
+       A module's index (PyModuleDef.m_base.m_index) is used to look up
+       the corresponding module object for this interpreter, if any.
+       (See PyState_FindModule().)  When any extension module
+       is initialized during import, its moduledef gets initialized by
+       PyModuleDef_Init(), and the first time that happens for each
+       PyModuleDef, its index gets set to the current value of
+       a global counter (see _PyRuntimeState.imports.last_module_index).
+       The entry for that index in this interpreter remains unset until
+       the module is actually imported here.  (Py_None is used as
+       a placeholder.)  Note that multi-phase init modules always get
+       an index for which there will never be a module set.
+
+       This is initialized lazily in PyState_AddModule(), which is also
+       where modules get added. */
+    PyObject *modules_by_index;
+    /* importlib module._bootstrap */
+    PyObject *importlib;
+    /* override for config->use_frozen_modules (for tests)
+       (-1: "off", 1: "on", 0: no override) */
+    int override_frozen_modules;
+    int override_multi_interp_extensions_check;
+#ifdef HAVE_DLOPEN
+    int dlopenflags;
+#endif
+    PyObject *import_func;
+    /* The global import lock. */
+    _PyRecursiveMutex lock;
+    /* diagnostic info in PyImport_ImportModuleLevelObject() */
+    struct {
+        int import_level;
+        PyTime_t accumulated;
+        int header;
+    } find_and_load;
+};
+
+
+
+/********** Interpreter state **************/
+
+#include "pycore_object_state.h"
+#include "pycore_crossinterp.h"
+
+
+struct _Py_long_state {
+    int max_str_digits;
+};
+
+struct codecs_state {
+    // A list of callable objects used to search for codecs.
+    PyObject *search_path;
+
+    // A dict mapping codec names to codecs returned from a callable in
+    // search_path.
+    PyObject *search_cache;
+
+    // A dict mapping error handling strategies to functions to implement them.
+    PyObject *error_registry;
+
+#ifdef Py_GIL_DISABLED
+    // Used to safely delete a specific item from search_path.
+    PyMutex search_path_mutex;
+#endif
+
+    // Whether or not the rest of the state is initialized.
+    int initialized;
+};
+
+// Support for stop-the-world events. This exists in both the PyRuntime struct
+// for global pauses and in each PyInterpreterState for per-interpreter pauses.
+struct _stoptheworld_state {
+    PyMutex mutex;       // Serializes stop-the-world attempts.
+
+    // NOTE: The below fields are protected by HEAD_LOCK(runtime), not by the
+    // above mutex.
+    bool requested;      // Set when a pause is requested.
+    bool world_stopped;  // Set when the world is stopped.
+    bool is_global;      // Set when contained in PyRuntime struct.
+
+    PyEvent stop_event;  // Set when thread_countdown reaches zero.
+    Py_ssize_t thread_countdown;  // Number of threads that must pause.
+
+    PyThreadState *requester; // Thread that requested the pause (may be NULL).
+};
+
+/* Tracks some rare events per-interpreter, used by the optimizer to turn 
on/off
+   specific optimizations. */
+typedef struct _rare_events {
+    /* Setting an object's class, obj.__class__ = ... */
+    uint8_t set_class;
+    /* Setting the bases of a class, cls.__bases__ = ... */
+    uint8_t set_bases;
+    /* Setting the PEP 523 frame eval function, 
_PyInterpreterState_SetFrameEvalFunc() */
+    uint8_t set_eval_frame_func;
+    /* Modifying the builtins,  __builtins__.__dict__[var] = ... */
+    uint8_t builtin_dict;
+    /* Modifying a function, e.g. func.__defaults__ = ..., etc. */
+    uint8_t func_modification;
+} _rare_events;
+
+struct
+Bigint {
+    struct Bigint *next;
+    int k, maxwds, sign, wds;
+    uint32_t x[1];
+};
+
+#if defined(Py_USING_MEMORY_DEBUGGER) || _PY_SHORT_FLOAT_REPR == 0
+
+struct _dtoa_state {
+    int _not_used;
+};
+
+#else  // !Py_USING_MEMORY_DEBUGGER && _PY_SHORT_FLOAT_REPR != 0
+
+/* The size of the Bigint freelist */
+#define Bigint_Kmax 7
+
+/* The size of the cached powers of 5 array */
+#define Bigint_Pow5size 8
+
+#ifndef PRIVATE_MEM
+#define PRIVATE_MEM 2304
+#endif
+#define Bigint_PREALLOC_SIZE \
+    ((PRIVATE_MEM+sizeof(double)-1)/sizeof(double))
+
+struct _dtoa_state {
+    // p5s is an array of powers of 5 of the form:
+    // 5**(2**(i+2)) for 0 <= i < Bigint_Pow5size
+    struct Bigint *p5s[Bigint_Pow5size];
+    // XXX This should be freed during runtime fini.
+    struct Bigint *freelist[Bigint_Kmax+1];
+    double preallocated[Bigint_PREALLOC_SIZE];
+    double *preallocated_next;
+};
+
+#endif  // !Py_USING_MEMORY_DEBUGGER
+
+struct _py_code_state {
+    PyMutex mutex;
+    // Interned constants from code objects. Used by the free-threaded build.
+    struct _Py_hashtable_t *constants;
+};
+
+#define FUNC_VERSION_CACHE_SIZE (1<<12)  /* Must be a power of 2 */
+
+struct _func_version_cache_item {
+    PyFunctionObject *func;
+    PyObject *code;
+};
+
+struct _py_func_state {
+#ifdef Py_GIL_DISABLED
+    // Protects next_version
+    PyMutex mutex;
+#endif
+
+    uint32_t next_version;
+    // Borrowed references to function and code objects whose
+    // func_version % FUNC_VERSION_CACHE_SIZE
+    // once was equal to the index in the table.
+    // They are cleared when the function or code object is deallocated.
+    struct _func_version_cache_item 
func_version_cache[FUNC_VERSION_CACHE_SIZE];
+};
+
+#include "pycore_dict_state.h"
+#include "pycore_exceptions.h"
+
+
+/****** type state *********/
+
+/* For now we hard-code this to a value for which we are confident
+   all the static builtin types will fit (for all builds). */
+#define _Py_MAX_MANAGED_STATIC_BUILTIN_TYPES 200
+#define _Py_MAX_MANAGED_STATIC_EXT_TYPES 10
+#define _Py_MAX_MANAGED_STATIC_TYPES \
+    (_Py_MAX_MANAGED_STATIC_BUILTIN_TYPES + _Py_MAX_MANAGED_STATIC_EXT_TYPES)
+
+struct _types_runtime_state {
+    /* Used to set PyTypeObject.tp_version_tag for core static types. */
+    // bpo-42745: next_version_tag remains shared by all interpreters
+    // because of static types.
+    unsigned int next_version_tag;
+
+    struct {
+        struct {
+            PyTypeObject *type;
+            int64_t interp_count;
+        } types[_Py_MAX_MANAGED_STATIC_TYPES];
+    } managed_static;
+};
+
+
+// Type attribute lookup cache: speed up attribute and method lookups,
+// see _PyType_Lookup().
+struct type_cache_entry {
+    unsigned int version;  // initialized from type->tp_version_tag
+#ifdef Py_GIL_DISABLED
+   _PySeqLock sequence;
+#endif
+    PyObject *name;        // reference to exactly a str or None
+    PyObject *value;       // borrowed reference or NULL
+};
+
+#define MCACHE_SIZE_EXP 12
+
+struct type_cache {
+    struct type_cache_entry hashtable[1 << MCACHE_SIZE_EXP];
+};
+
+typedef struct {
+    PyTypeObject *type;
+    int isbuiltin;
+    int readying;
+    int ready;
+    // XXX tp_dict can probably be statically allocated,
+    // instead of dynamically and stored on the interpreter.
+    PyObject *tp_dict;
+    PyObject *tp_subclasses;
+    /* We never clean up weakrefs for static builtin types since
+       they will effectively never get triggered.  However, there
+       are also some diagnostic uses for the list of weakrefs,
+       so we still keep it. */
+    PyObject *tp_weaklist;
+} managed_static_type_state;
+
+#define TYPE_VERSION_CACHE_SIZE (1<<12)  /* Must be a power of 2 */
+
+struct types_state {
+    /* Used to set PyTypeObject.tp_version_tag.
+       It starts at _Py_MAX_GLOBAL_TYPE_VERSION_TAG + 1,
+       where all those lower numbers are used for core static types. */
+    unsigned int next_version_tag;
+
+    struct type_cache type_cache;
+
+    /* Every static builtin type is initialized for each interpreter
+       during its own initialization, including for the main interpreter
+       during global runtime initialization.  This is done by calling
+       _PyStaticType_InitBuiltin().
+
+       The first time a static builtin type is initialized, all the
+       normal PyType_Ready() stuff happens.  The only difference from
+       normal is that there are three PyTypeObject fields holding
+       objects which are stored here (on PyInterpreterState) rather
+       than in the corresponding PyTypeObject fields.  Those are:
+       tp_dict (cls.__dict__), tp_subclasses (cls.__subclasses__),
+       and tp_weaklist.
+
+       When a subinterpreter is initialized, each static builtin type
+       is still initialized, but only the interpreter-specific portion,
+       namely those three objects.
+
+       Those objects are stored in the PyInterpreterState.types.builtins
+       array, at the index corresponding to each specific static builtin
+       type.  That index (a size_t value) is stored in the tp_subclasses
+       field.  For static builtin types, we re-purposed the now-unused
+       tp_subclasses to avoid adding another field to PyTypeObject.
+       In all other cases tp_subclasses holds a dict like before.
+       (The field was previously defined as PyObject*, but is now void*
+       to reflect its dual use.)
+
+       The index for each static builtin type isn't statically assigned.
+       Instead it is calculated the first time a type is initialized
+       (by the main interpreter).  The index matches the order in which
+       the type was initialized relative to the others.  The actual
+       value comes from the current value of num_builtins_initialized,
+       as each type is initialized for the main interpreter.
+
+       num_builtins_initialized is incremented once for each static
+       builtin type.  Once initialization is over for a subinterpreter,
+       the value will be the same as for all other interpreters.  */
+    struct {
+        size_t num_initialized;
+        managed_static_type_state 
initialized[_Py_MAX_MANAGED_STATIC_BUILTIN_TYPES];
+    } builtins;
+    /* We apply a similar strategy for managed extension modules. */
+    struct {
+        size_t num_initialized;
+        size_t next_index;
+        managed_static_type_state 
initialized[_Py_MAX_MANAGED_STATIC_EXT_TYPES];
+    } for_extensions;
+    PyMutex mutex;
+
+    // Borrowed references to type objects whose
+    // tp_version_tag % TYPE_VERSION_CACHE_SIZE
+    // once was equal to the index in the table.
+    // They are cleared when the type object is deallocated.
+    PyTypeObject *type_version_cache[TYPE_VERSION_CACHE_SIZE];
+};
+
+struct _warnings_runtime_state {
+    /* Both 'filters' and 'onceregistry' can be set in warnings.py;
+       get_warnings_attr() will reset these variables accordingly. */
+    PyObject *filters;  /* List */
+    PyObject *once_registry;  /* Dict */
+    PyObject *default_action; /* String */
+    _PyRecursiveMutex lock;
+    long filters_version;
+};
+
+struct _Py_mem_interp_free_queue {
+    int has_work;   // true if the queue is not empty
+    PyMutex mutex;  // protects the queue
+    struct llist_node head;  // queue of _mem_work_chunk items
+};
+
+
+/****** Unicode state *********/
+
+typedef enum {
+    _Py_ERROR_UNKNOWN=0,
+    _Py_ERROR_STRICT,
+    _Py_ERROR_SURROGATEESCAPE,
+    _Py_ERROR_REPLACE,
+    _Py_ERROR_IGNORE,
+    _Py_ERROR_BACKSLASHREPLACE,
+    _Py_ERROR_SURROGATEPASS,
+    _Py_ERROR_XMLCHARREFREPLACE,
+    _Py_ERROR_OTHER
+} _Py_error_handler;
+
+struct _Py_unicode_runtime_ids {
+    PyMutex mutex;
+    // next_index value must be preserved when Py_Initialize()/Py_Finalize()
+    // is called multiple times: see _PyUnicode_FromId() implementation.
+    Py_ssize_t next_index;
+};
+
+struct _Py_unicode_runtime_state {
+    struct _Py_unicode_runtime_ids ids;
+};
+
+/* fs_codec.encoding is initialized to NULL.
+   Later, it is set to a non-NULL string by _PyUnicode_InitEncodings(). */
+struct _Py_unicode_fs_codec {
+    char *encoding;   // Filesystem encoding (encoded to UTF-8)
+    int utf8;         // encoding=="utf-8"?
+    char *errors;     // Filesystem errors (encoded to UTF-8)
+    _Py_error_handler error_handler;
+};
+
+struct _Py_unicode_ids {
+    Py_ssize_t size;
+    PyObject **array;
+};
+
+#include "pycore_ucnhash.h"
+
+struct _Py_unicode_state {
+    struct _Py_unicode_fs_codec fs_codec;
+
+    _PyUnicode_Name_CAPI *ucnhash_capi;
+
+    // Unicode identifiers (_Py_Identifier): see _PyUnicode_FromId()
+    struct _Py_unicode_ids ids;
+};
+
+// Borrowed references to common callables:
+struct callable_cache {
+    PyObject *isinstance;
+    PyObject *len;
+    PyObject *list_append;
+    PyObject *object__getattribute__;
+};
+
+#include "pycore_obmalloc.h"
+
+/* Length of array of slotdef pointers used to store slots with the
+   same __name__.  There should be at most MAX_EQUIV-1 slotdef entries with
+   the same __name__, for any __name__. Since that's a static property, it is
+   appropriate to declare fixed-size arrays for this. */
+#define MAX_EQUIV 10
+
+typedef struct wrapperbase pytype_slotdef;
+
+
+struct _Py_interp_cached_objects {
+#ifdef Py_GIL_DISABLED
+    PyMutex interned_mutex;
+#endif
+    PyObject *interned_strings;
+
+    /* object.__reduce__ */
+    PyObject *objreduce;
+    PyObject *type_slots_pname;
+    pytype_slotdef *type_slots_ptrs[MAX_EQUIV];
+
+    /* TypeVar and related types */
+    PyTypeObject *generic_type;
+    PyTypeObject *typevar_type;
+    PyTypeObject *typevartuple_type;
+    PyTypeObject *paramspec_type;
+    PyTypeObject *paramspecargs_type;
+    PyTypeObject *paramspeckwargs_type;
+    PyTypeObject *constevaluator_type;
+};
+
+struct _Py_interp_static_objects {
+    struct {
+        int _not_used;
+        // hamt_empty is here instead of global because of its weakreflist.
+        _PyGC_Head_UNUSED _hamt_empty_gc_not_used;
+        PyHamtObject hamt_empty;
+        PyBaseExceptionObject last_resort_memory_error;
+    } singletons;
+};
+
+#include "pycore_instruments.h"
+
+
+#ifdef Py_GIL_DISABLED
+
+// A min-heap of indices
+typedef struct _PyIndexHeap {
+    int32_t *values;
+
+    // Number of items stored in values
+    Py_ssize_t size;
+
+    // Maximum number of items that can be stored in values
+    Py_ssize_t capacity;
+} _PyIndexHeap;
+
+// An unbounded pool of indices. Indices are allocated starting from 0. They
+// may be released back to the pool once they are no longer in use.
+typedef struct _PyIndexPool {
+    PyMutex mutex;
+
+    // Min heap of indices available for allocation
+    _PyIndexHeap free_indices;
+
+    // Next index to allocate if no free indices are available
+    int32_t next_index;
+} _PyIndexPool;
+
+typedef union _Py_unique_id_entry {
+    // Points to the next free type id, when part of the freelist
+    union _Py_unique_id_entry *next;
+
+    // Stores the object when the id is assigned
+    PyObject *obj;
+} _Py_unique_id_entry;
+
+struct _Py_unique_id_pool {
+    PyMutex mutex;
+
+    // combined table of object with allocated unique ids and unallocated ids.
+    _Py_unique_id_entry *table;
+
+    // Next entry to allocate inside 'table' or NULL
+    _Py_unique_id_entry *freelist;
+
+    // size of 'table'
+    Py_ssize_t size;
+};
+
+#endif
+
+
+/* PyInterpreterState holds the global state for one of the runtime's
+   interpreters.  Typically the initial (main) interpreter is the only one.
+
+   The PyInterpreterState typedef is in Include/pytypedefs.h.
+   */
+struct _is {
+
+    /* This struct contains the eval_breaker,
+     * which is by far the hottest field in this struct
+     * and should be placed at the beginning. */
+    struct _ceval_state ceval;
+
+    PyInterpreterState *next;
+
+    int64_t id;
+    Py_ssize_t id_refcount;
+    int requires_idref;
+
+    long _whence;
+
+    /* Has been initialized to a safe state.
+
+       In order to be effective, this must be set to 0 during or right
+       after allocation. */
+    int _initialized;
+    /* Has been fully initialized via pylifecycle.c. */
+    int _ready;
+    int finalizing;
+
+    uintptr_t last_restart_version;
+    struct pythreads {
+        uint64_t next_unique_id;
+        /* The linked list of threads, newest first. */
+        PyThreadState *head;
+        _PyThreadStateImpl *preallocated;
+        /* The thread currently executing in the __main__ module, if any. */
+        PyThreadState *main;
+        /* Used in Modules/_threadmodule.c. */
+        Py_ssize_t count;
+        /* Support for runtime thread stack size tuning.
+           A value of 0 means using the platform's default stack size
+           or the size specified by the THREAD_STACK_SIZE macro. */
+        /* Used in Python/thread.c. */
+        size_t stacksize;
+    } threads;
+
+    /* Reference to the _PyRuntime global variable. This field exists
+       to not have to pass runtime in addition to tstate to a function.
+       Get runtime from tstate: tstate->interp->runtime. */
+    struct pyruntimestate *runtime;
+
+    /* Set by Py_EndInterpreter().
+
+       Use _PyInterpreterState_GetFinalizing()
+       and _PyInterpreterState_SetFinalizing()
+       to access it, don't access it directly. */
+    PyThreadState* _finalizing;
+    /* The ID of the OS thread in which we are finalizing. */
+    unsigned long _finalizing_id;
+
+    struct _gc_runtime_state gc;
+
+    /* The following fields are here to avoid allocation during init.
+       The data is exposed through PyInterpreterState pointer fields.
+       These fields should not be accessed directly outside of init.
+
+       All other PyInterpreterState pointer fields are populated when
+       needed and default to NULL.
+
+       For now there are some exceptions to that rule, which require
+       allocation during init.  These will be addressed on a case-by-case
+       basis.  Also see _PyRuntimeState regarding the various mutex fields.
+       */
+
+    // Dictionary of the sys module
+    PyObject *sysdict;
+
+    // Dictionary of the builtins module
+    PyObject *builtins;
+
+    struct _import_state imports;
+
+    /* The per-interpreter GIL, which might not be used. */
+    struct _gil_runtime_state _gil;
+
+     /* ---------- IMPORTANT ---------------------------
+     The fields above this line are declared as early as
+     possible to facilitate out-of-process observability
+     tools. */
+
+    struct codecs_state codecs;
+
+    PyConfig config;
+    unsigned long feature_flags;
+
+    PyObject *dict;  /* Stores per-interpreter state */
+
+    PyObject *sysdict_copy;
+    PyObject *builtins_copy;
+    // Initialized to _PyEval_EvalFrameDefault().
+    _PyFrameEvalFunction eval_frame;
+
+    PyFunction_WatchCallback func_watchers[FUNC_MAX_WATCHERS];
+    // One bit is set for each non-NULL entry in func_watchers
+    uint8_t active_func_watchers;
+
+    Py_ssize_t co_extra_user_count;
+    freefunc co_extra_freefuncs[MAX_CO_EXTRA_USERS];
+
+    /* cross-interpreter data and utils */
+    _PyXI_state_t xi;
+
+#ifdef HAVE_FORK
+    PyObject *before_forkers;
+    PyObject *after_forkers_parent;
+    PyObject *after_forkers_child;
+#endif
+
+    struct _warnings_runtime_state warnings;
+    struct atexit_state atexit;
+    struct _stoptheworld_state stoptheworld;
+    struct _qsbr_shared qsbr;
+
+#if defined(Py_GIL_DISABLED)
+    struct _mimalloc_interp_state mimalloc;
+    struct _brc_state brc;  // biased reference counting state
+    struct _Py_unique_id_pool unique_ids;  // object ids for per-thread 
refcounts
+    PyMutex weakref_locks[NUM_WEAKREF_LIST_LOCKS];
+    _PyIndexPool tlbc_indices;
+#endif
+    // Per-interpreter list of tasks, any lingering tasks from thread
+    // states gets added here and removed from the corresponding
+    // thread state's list.
+    struct llist_node asyncio_tasks_head;
+    // `asyncio_tasks_lock` is used when tasks are moved
+    // from thread's list to interpreter's list.
+    PyMutex asyncio_tasks_lock;
+
+    // Per-interpreter state for the obmalloc allocator.  For the main
+    // interpreter and for all interpreters that don't have their
+    // own obmalloc state, this points to the static structure in
+    // obmalloc.c obmalloc_state_main.  For other interpreters, it is
+    // heap allocated by _PyMem_init_obmalloc() and freed when the
+    // interpreter structure is freed.  In the case of a heap allocated
+    // obmalloc state, it is not safe to hold on to or use memory after
+    // the interpreter is freed. The obmalloc state corresponding to
+    // that allocated memory is gone.  See free_obmalloc_arenas() for
+    // more comments.
+    struct _obmalloc_state *obmalloc;
+
+    PyObject *audit_hooks;
+    PyType_WatchCallback type_watchers[TYPE_MAX_WATCHERS];
+    PyCode_WatchCallback code_watchers[CODE_MAX_WATCHERS];
+    PyContext_WatchCallback context_watchers[CONTEXT_MAX_WATCHERS];
+    // One bit is set for each non-NULL entry in code_watchers
+    uint8_t active_code_watchers;
+    uint8_t active_context_watchers;
+
+    struct _py_object_state object_state;
+    struct _Py_unicode_state unicode;
+    struct _Py_long_state long_state;
+    struct _dtoa_state dtoa;
+    struct _py_func_state func_state;
+    struct _py_code_state code_state;
+
+    struct _Py_dict_state dict_state;
+    struct _Py_exc_state exc_state;
+    struct _Py_mem_interp_free_queue mem_free_queue;
+
+    struct ast_state ast;
+    struct types_state types;
+    struct callable_cache callable_cache;
+    bool jit;
+    struct _PyExecutorObject *executor_list_head;
+    size_t trace_run_counter;
+    _rare_events rare_events;
+    PyDict_WatchCallback builtins_dict_watcher;
+
+    _Py_GlobalMonitors monitors;
+    bool sys_profile_initialized;
+    bool sys_trace_initialized;
+    Py_ssize_t sys_profiling_threads; /* Count of threads with c_profilefunc 
set */
+    Py_ssize_t sys_tracing_threads; /* Count of threads with c_tracefunc set */
+    PyObject 
*monitoring_callables[PY_MONITORING_TOOL_IDS][_PY_MONITORING_EVENTS];
+    PyObject *monitoring_tool_names[PY_MONITORING_TOOL_IDS];
+    uintptr_t monitoring_tool_versions[PY_MONITORING_TOOL_IDS];
+
+    struct _Py_interp_cached_objects cached_objects;
+    struct _Py_interp_static_objects static_objects;
+
+    Py_ssize_t _interactive_src_count;
+
+    /* the initial PyInterpreterState.threads.head */
+    _PyThreadStateImpl _initial_thread;
+    // _initial_thread should be the last field of PyInterpreterState.
+    // See https://github.com/python/cpython/issues/127117.
+
+#if !defined(Py_GIL_DISABLED) && defined(Py_STACKREF_DEBUG)
+    uint64_t next_stackref;
+    _Py_hashtable_t *open_stackrefs_table;
+#  ifdef Py_STACKREF_CLOSE_DEBUG
+    _Py_hashtable_t *closed_stackrefs_table;
+#  endif
+#endif
+};
+
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* Py_INTERNAL_INTERP_STRUCTS_H */
diff --git a/Include/internal/pycore_long.h b/Include/internal/pycore_long.h
index df0656a7cb8f0c..7b7f4e79ec653d 100644
--- a/Include/internal/pycore_long.h
+++ b/Include/internal/pycore_long.h
@@ -9,7 +9,8 @@ extern "C" {
 #endif
 
 #include "pycore_bytesobject.h"   // _PyBytesWriter
-#include "pycore_global_objects.h"// _PY_NSMALLNEGINTS
+#include "pycore_runtime_structs.h"// _PY_NSMALLNEGINTS
+#include "pycore_global_objects.h"// _PY_SINGLETON
 #include "pycore_runtime.h"       // _PyRuntime
 
 /*
diff --git a/Include/internal/pycore_parser.h b/Include/internal/pycore_parser.h
index b16084aaa15515..2885dee63dcf94 100644
--- a/Include/internal/pycore_parser.h
+++ b/Include/internal/pycore_parser.h
@@ -13,23 +13,6 @@ extern "C" {
 #include "pycore_global_strings.h"  // _Py_DECLARE_STR()
 #include "pycore_pyarena.h"         // PyArena
 
-
-#ifdef Py_DEBUG
-#define _PYPEGEN_NSTATISTICS 2000
-#endif
-
-struct _parser_runtime_state {
-#ifdef Py_DEBUG
-    long memo_statistics[_PYPEGEN_NSTATISTICS];
-#ifdef Py_GIL_DISABLED
-    PyMutex mutex;
-#endif
-#else
-    int _not_used;
-#endif
-    struct _expr dummy_name;
-};
-
 _Py_DECLARE_STR(empty, "")
 #if defined(Py_DEBUG) && defined(Py_GIL_DISABLED)
 #define _parser_runtime_state_INIT \
diff --git a/Include/internal/pycore_pyhash.h b/Include/internal/pycore_pyhash.h
index 9414e7761171d2..84cb72fa6fd1b2 100644
--- a/Include/internal/pycore_pyhash.h
+++ b/Include/internal/pycore_pyhash.h
@@ -71,19 +71,6 @@ extern int _Py_HashSecret_Initialized;
 #endif
 
 
-struct pyhash_runtime_state {
-    struct {
-#ifndef MS_WINDOWS
-        int fd;
-        dev_t st_dev;
-        ino_t st_ino;
-#else
-    // This is a placeholder so the struct isn't empty on Windows.
-    int _not_used;
-#endif
-    } urandom_cache;
-};
-
 #ifndef MS_WINDOWS
 # define _py_urandom_cache_INIT \
     { \
diff --git a/Include/internal/pycore_pymem.h b/Include/internal/pycore_pymem.h
index 5386d4c5f83031..aa20190e252a2d 100644
--- a/Include/internal/pycore_pymem.h
+++ b/Include/internal/pycore_pymem.h
@@ -27,34 +27,6 @@ PyAPI_FUNC(char*) _PyMem_Strdup(const char *str);
 // wcsdup() using PyMem_RawMalloc()
 extern wchar_t* _PyMem_RawWcsdup(const wchar_t *str);
 
-typedef struct {
-    /* We tag each block with an API ID in order to tag API violations */
-    char api_id;
-    PyMemAllocatorEx alloc;
-} debug_alloc_api_t;
-
-struct _pymem_allocators {
-    PyMutex mutex;
-    struct {
-        PyMemAllocatorEx raw;
-        PyMemAllocatorEx mem;
-        PyMemAllocatorEx obj;
-    } standard;
-    struct {
-        debug_alloc_api_t raw;
-        debug_alloc_api_t mem;
-        debug_alloc_api_t obj;
-    } debug;
-    int is_debug_enabled;
-    PyObjectArenaAllocator obj_arena;
-};
-
-struct _Py_mem_interp_free_queue {
-    int has_work;   // true if the queue is not empty
-    PyMutex mutex;  // protects the queue
-    struct llist_node head;  // queue of _mem_work_chunk items
-};
-
 /* Special bytes broadcast into debug memory blocks at appropriate times.
    Strings of these are unlikely to be valid addresses, floats, ints or
    7-bit ASCII.
diff --git a/Include/internal/pycore_pystate.h 
b/Include/internal/pycore_pystate.h
index f3667a8aa71c27..d4867a7b96bfeb 100644
--- a/Include/internal/pycore_pystate.h
+++ b/Include/internal/pycore_pystate.h
@@ -8,8 +8,10 @@ extern "C" {
 #  error "this header requires Py_BUILD_CORE define"
 #endif
 
-#include "pycore_runtime.h"       // _PyRuntime
-#include "pycore_tstate.h"        // _PyThreadStateImpl
+#include "pycore_runtime_structs.h" // _PyRuntime
+#include "pycore_runtime.h"         // _PyRuntimeState_GetFinalizing
+#include "pycore_tstate.h"          // _PyThreadStateImpl
+#include "pycore_interp.h"          // _PyInterpreterState_GetConfig
 
 // Values for PyThreadState.state. A thread must be in the "attached" state
 // before calling most Python APIs. If the GIL is enabled, then "attached"
diff --git a/Include/internal/pycore_pythread.h 
b/Include/internal/pycore_pythread.h
index a1e084cf67d58d..8457b07340f8dd 100644
--- a/Include/internal/pycore_pythread.h
+++ b/Include/internal/pycore_pythread.h
@@ -8,8 +8,8 @@ extern "C" {
 #  error "this header requires Py_BUILD_CORE define"
 #endif
 
-#include "dynamic_annotations.h" // _Py_ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX
-#include "pycore_llist.h"        // struct llist_node
+#include "dynamic_annotations.h"     // _Py_ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX
+#include "pycore_llist.h"            // struct llist_node
 
 // Get _POSIX_THREADS and _POSIX_SEMAPHORES macros if available
 #if (defined(HAVE_UNISTD_H) && !defined(_POSIX_THREADS) \
diff --git a/Include/internal/pycore_runtime.h 
b/Include/internal/pycore_runtime.h
index 9324ce9eb89d2c..bda8a30dd1d0ad 100644
--- a/Include/internal/pycore_runtime.h
+++ b/Include/internal/pycore_runtime.h
@@ -8,201 +8,9 @@ extern "C" {
 #  error "this header requires Py_BUILD_CORE define"
 #endif
 
-#include "pycore_atexit.h"          // struct _atexit_runtime_state
-#include "pycore_audit.h"           // _Py_AuditHookEntry
-#include "pycore_ceval_state.h"     // struct _ceval_runtime_state
-#include "pycore_crossinterp.h"     // _PyXI_global_state_t
-#include "pycore_debug_offsets.h"   // _Py_DebugOffsets
-#include "pycore_faulthandler.h"    // struct _faulthandler_runtime_state
-#include "pycore_floatobject.h"     // struct _Py_float_runtime_state
-#include "pycore_import.h"          // struct _import_runtime_state
-#include "pycore_interp.h"          // PyInterpreterState
-#include "pycore_object_state.h"    // struct _py_object_runtime_state
-#include "pycore_parser.h"          // struct _parser_runtime_state
-#include "pycore_pyhash.h"          // struct pyhash_runtime_state
-#include "pycore_pymem.h"           // struct _pymem_allocators
-#include "pycore_pythread.h"        // struct _pythread_runtime_state
-#include "pycore_signal.h"          // struct _signals_runtime_state
-#include "pycore_time.h"            // struct _PyTime_runtime_state
-#include "pycore_tracemalloc.h"     // struct _tracemalloc_runtime_state
-#include "pycore_typeobject.h"      // struct _types_runtime_state
-#include "pycore_unicodeobject.h"   // struct _Py_unicode_runtime_state
+#include "pycore_runtime_structs.h"
 
-
-/* Full Python runtime state */
-
-/* _PyRuntimeState holds the global state for the CPython runtime.
-   That data is exported by the internal API as a global variable
-   (_PyRuntime, defined near the top of pylifecycle.c).
-   */
-typedef struct pyruntimestate {
-    /* This field must be first to facilitate locating it by out of process
-     * debuggers. Out of process debuggers will use the offsets contained in 
this
-     * field to be able to locate other fields in several interpreter 
structures
-     * in a way that doesn't require them to know the exact layout of those
-     * structures.
-     *
-     * IMPORTANT:
-     * This struct is **NOT** backwards compatible between minor version of the
-     * interpreter and the members, order of members and size can change 
between
-     * minor versions. This struct is only guaranteed to be stable between 
patch
-     * versions for a given minor version of the interpreter.
-     */
-    _Py_DebugOffsets debug_offsets;
-
-    /* Has been initialized to a safe state.
-
-       In order to be effective, this must be set to 0 during or right
-       after allocation. */
-    int _initialized;
-
-    /* Is running Py_PreInitialize()? */
-    int preinitializing;
-
-    /* Is Python preinitialized? Set to 1 by Py_PreInitialize() */
-    int preinitialized;
-
-    /* Is Python core initialized? Set to 1 by _Py_InitializeCore() */
-    int core_initialized;
-
-    /* Is Python fully initialized? Set to 1 by Py_Initialize() */
-    int initialized;
-
-    /* Set by Py_FinalizeEx(). Only reset to NULL if Py_Initialize()
-       is called again.
-
-       Use _PyRuntimeState_GetFinalizing() and _PyRuntimeState_SetFinalizing()
-       to access it, don't access it directly. */
-    PyThreadState *_finalizing;
-    /* The ID of the OS thread in which we are finalizing. */
-    unsigned long _finalizing_id;
-
-    struct pyinterpreters {
-        PyMutex mutex;
-        /* The linked list of interpreters, newest first. */
-        PyInterpreterState *head;
-        /* The runtime's initial interpreter, which has a special role
-           in the operation of the runtime.  It is also often the only
-           interpreter. */
-        PyInterpreterState *main;
-        /* next_id is an auto-numbered sequence of small
-           integers.  It gets initialized in _PyInterpreterState_Enable(),
-           which is called in Py_Initialize(), and used in
-           PyInterpreterState_New().  A negative interpreter ID
-           indicates an error occurred.  The main interpreter will
-           always have an ID of 0.  Overflow results in a RuntimeError.
-           If that becomes a problem later then we can adjust, e.g. by
-           using a Python int. */
-        int64_t next_id;
-    } interpreters;
-
-    /* Platform-specific identifier and PyThreadState, respectively, for the
-       main thread in the main interpreter. */
-    unsigned long main_thread;
-    PyThreadState *main_tstate;
-
-    /* ---------- IMPORTANT ---------------------------
-     The fields above this line are declared as early as
-     possible to facilitate out-of-process observability
-     tools. */
-
-    /* cross-interpreter data and utils */
-    _PyXI_global_state_t xi;
-
-    struct _pymem_allocators allocators;
-    struct _obmalloc_global_state obmalloc;
-    struct pyhash_runtime_state pyhash_state;
-    struct _pythread_runtime_state threads;
-    struct _signals_runtime_state signals;
-
-    /* Used for the thread state bound to the current thread. */
-    Py_tss_t autoTSSkey;
-
-    /* Used instead of PyThreadState.trash when there is not current tstate. */
-    Py_tss_t trashTSSkey;
-
-    PyWideStringList orig_argv;
-
-    struct _parser_runtime_state parser;
-
-    struct _atexit_runtime_state atexit;
-
-    struct _import_runtime_state imports;
-    struct _ceval_runtime_state ceval;
-    struct _gilstate_runtime_state {
-        /* bpo-26558: Flag to disable PyGILState_Check().
-           If set to non-zero, PyGILState_Check() always return 1. */
-        int check_enabled;
-        /* The single PyInterpreterState used by this process'
-           GILState implementation
-        */
-        /* TODO: Given interp_main, it may be possible to kill this ref */
-        PyInterpreterState *autoInterpreterState;
-    } gilstate;
-    struct _getargs_runtime_state {
-        struct _PyArg_Parser *static_parsers;
-    } getargs;
-    struct _fileutils_state fileutils;
-    struct _faulthandler_runtime_state faulthandler;
-    struct _tracemalloc_runtime_state tracemalloc;
-    struct _reftracer_runtime_state ref_tracer;
-
-    // The rwmutex is used to prevent overlapping global and per-interpreter
-    // stop-the-world events. Global stop-the-world events lock the mutex
-    // exclusively (as a "writer"), while per-interpreter stop-the-world events
-    // lock it non-exclusively (as "readers").
-    _PyRWMutex stoptheworld_mutex;
-    struct _stoptheworld_state stoptheworld;
-
-    PyPreConfig preconfig;
-
-    // Audit values must be preserved when Py_Initialize()/Py_Finalize()
-    // is called multiple times.
-    Py_OpenCodeHookFunction open_code_hook;
-    void *open_code_userdata;
-    struct {
-        PyMutex mutex;
-        _Py_AuditHookEntry *head;
-    } audit_hooks;
-
-    struct _py_object_runtime_state object_state;
-    struct _Py_float_runtime_state float_state;
-    struct _Py_unicode_runtime_state unicode_state;
-    struct _types_runtime_state types;
-    struct _Py_time_runtime_state time;
-
-#if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE)
-    // Used in "Python/emscripten_trampoline.c" to choose between type
-    // reflection trampoline and EM_JS trampoline.
-    int (*emscripten_count_args_function)(PyCFunctionWithKeywords func);
-#endif
-
-    /* All the objects that are shared by the runtime's interpreters. */
-    struct _Py_cached_objects cached_objects;
-    struct _Py_static_objects static_objects;
-
-    /* The following fields are here to avoid allocation during init.
-       The data is exposed through _PyRuntimeState pointer fields.
-       These fields should not be accessed directly outside of init.
-
-       All other _PyRuntimeState pointer fields are populated when
-       needed and default to NULL.
-
-       For now there are some exceptions to that rule, which require
-       allocation during init.  These will be addressed on a case-by-case
-       basis.  Most notably, we don't pre-allocated the several mutex
-       (PyThread_type_lock) fields, because on Windows we only ever get
-       a pointer type.
-       */
-
-    /* _PyRuntimeState.interpreters.main */
-    PyInterpreterState _main_interpreter;
-    // _main_interpreter should be the last field of _PyRuntimeState.
-    // See https://github.com/python/cpython/issues/127117.
-} _PyRuntimeState;
-
-
-/* other API */
+/* API */
 
 // Export _PyRuntime for shared extensions which use it in static inline
 // functions for best performance, like _Py_IsMainThread() or _Py_ID().
diff --git a/Include/internal/pycore_runtime_init.h 
b/Include/internal/pycore_runtime_init.h
index 2ec32b64adde0b..5ea9b296feba75 100644
--- a/Include/internal/pycore_runtime_init.h
+++ b/Include/internal/pycore_runtime_init.h
@@ -8,11 +8,13 @@ extern "C" {
 #  error "this header requires Py_BUILD_CORE define"
 #endif
 
+#include "pycore_structs.h"
 #include "pycore_ceval_state.h"   // _PyEval_RUNTIME_PERF_INIT
 #include "pycore_debug_offsets.h"  // _Py_DebugOffsets_INIT()
 #include "pycore_faulthandler.h"  // _faulthandler_runtime_state_INIT
 #include "pycore_floatobject.h"   // _py_float_format_unknown
 #include "pycore_function.h"
+#include "pycore_hamt.h"          // _PyHamt_BitmapNode_Type
 #include "pycore_object.h"        // _PyObject_HEAD_INIT
 #include "pycore_obmalloc_init.h" // _obmalloc_global_state_INIT
 #include "pycore_parser.h"        // _parser_runtime_state_INIT
diff --git a/Include/internal/pycore_runtime_structs.h 
b/Include/internal/pycore_runtime_structs.h
new file mode 100644
index 00000000000000..2ea1d795f24789
--- /dev/null
+++ b/Include/internal/pycore_runtime_structs.h
@@ -0,0 +1,317 @@
+#ifndef Py_INTERNAL_RUNTIME_STRUCTS_H
+#define Py_INTERNAL_RUNTIME_STRUCTS_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* This file contains the struct definitions for the runtime, interpreter
+ * and thread states, plus all smaller structs contained therein */
+
+#include "pycore_structs.h"
+#include "pycore_interp_structs.h"
+
+/************ Runtime state ************/
+
+typedef struct {
+    /* We tag each block with an API ID in order to tag API violations */
+    char api_id;
+    PyMemAllocatorEx alloc;
+} debug_alloc_api_t;
+
+struct _pymem_allocators {
+    PyMutex mutex;
+    struct {
+        PyMemAllocatorEx raw;
+        PyMemAllocatorEx mem;
+        PyMemAllocatorEx obj;
+    } standard;
+    struct {
+        debug_alloc_api_t raw;
+        debug_alloc_api_t mem;
+        debug_alloc_api_t obj;
+    } debug;
+    int is_debug_enabled;
+    PyObjectArenaAllocator obj_arena;
+};
+
+enum _py_float_format_type {
+    _py_float_format_unknown,
+    _py_float_format_ieee_big_endian,
+    _py_float_format_ieee_little_endian,
+};
+
+struct _Py_float_runtime_state {
+    enum _py_float_format_type float_format;
+    enum _py_float_format_type double_format;
+};
+
+struct pyhash_runtime_state {
+    struct {
+#ifndef MS_WINDOWS
+        int fd;
+        dev_t st_dev;
+        ino_t st_ino;
+#else
+    // This is a placeholder so the struct isn't empty on Windows.
+    int _not_used;
+#endif
+    } urandom_cache;
+};
+
+#include "pycore_tracemalloc.h"
+
+struct _fileutils_state {
+    int force_ascii;
+};
+
+#include "pycore_debug_offsets.h"
+#include "pycore_signal.h"
+#include "pycore_faulthandler.h"
+#include "pycore_pythread.h"
+#include "pycore_ast.h"
+
+#ifdef Py_DEBUG
+#define _PYPEGEN_NSTATISTICS 2000
+#endif
+
+struct _parser_runtime_state {
+#ifdef Py_DEBUG
+    long memo_statistics[_PYPEGEN_NSTATISTICS];
+#ifdef Py_GIL_DISABLED
+    PyMutex mutex;
+#endif
+#else
+    int _not_used;
+#endif
+    struct _expr dummy_name;
+};
+
+typedef struct {
+    PyTime_t numer;
+    PyTime_t denom;
+} _PyTimeFraction;
+
+struct _Py_time_runtime_state {
+#if defined(MS_WINDOWS) || defined(__APPLE__)
+    _PyTimeFraction base;
+#else
+    char _unused;
+#endif
+};
+
+
+struct _Py_cached_objects {
+    // XXX We could statically allocate the hashtable.
+    _Py_hashtable_t *interned_strings;
+};
+
+// These would be in pycore_long.h if it weren't for an include cycle.
+#define _PY_NSMALLPOSINTS           257
+#define _PY_NSMALLNEGINTS           5
+
+#include "pycore_global_strings.h"
+
+struct _Py_static_objects {
+    struct {
+        /* Small integers are preallocated in this array so that they
+         * can be shared.
+         * The integers that are preallocated are those in the range
+         * -_PY_NSMALLNEGINTS (inclusive) to _PY_NSMALLPOSINTS (exclusive).
+         */
+        PyLongObject small_ints[_PY_NSMALLNEGINTS + _PY_NSMALLPOSINTS];
+
+        PyBytesObject bytes_empty;
+        struct {
+            PyBytesObject ob;
+            char eos;
+        } bytes_characters[256];
+
+        struct _Py_global_strings strings;
+
+        _PyGC_Head_UNUSED _tuple_empty_gc_not_used;
+        PyTupleObject tuple_empty;
+
+        _PyGC_Head_UNUSED _hamt_bitmap_node_empty_gc_not_used;
+        PyHamtNode_Bitmap hamt_bitmap_node_empty;
+        _PyContextTokenMissing context_token_missing;
+    } singletons;
+};
+
+/* Full Python runtime state */
+
+/* _PyRuntimeState holds the global state for the CPython runtime.
+   That data is exported by the internal API as a global variable
+   (_PyRuntime, defined near the top of pylifecycle.c).
+   */
+typedef struct pyruntimestate {
+    /* This field must be first to facilitate locating it by out of process
+     * debuggers. Out of process debuggers will use the offsets contained in 
this
+     * field to be able to locate other fields in several interpreter 
structures
+     * in a way that doesn't require them to know the exact layout of those
+     * structures.
+     *
+     * IMPORTANT:
+     * This struct is **NOT** backwards compatible between minor version of the
+     * interpreter and the members, order of members and size can change 
between
+     * minor versions. This struct is only guaranteed to be stable between 
patch
+     * versions for a given minor version of the interpreter.
+     */
+    _Py_DebugOffsets debug_offsets;
+
+    /* Has been initialized to a safe state.
+
+       In order to be effective, this must be set to 0 during or right
+       after allocation. */
+    int _initialized;
+
+    /* Is running Py_PreInitialize()? */
+    int preinitializing;
+
+    /* Is Python preinitialized? Set to 1 by Py_PreInitialize() */
+    int preinitialized;
+
+    /* Is Python core initialized? Set to 1 by _Py_InitializeCore() */
+    int core_initialized;
+
+    /* Is Python fully initialized? Set to 1 by Py_Initialize() */
+    int initialized;
+
+    /* Set by Py_FinalizeEx(). Only reset to NULL if Py_Initialize()
+       is called again.
+
+       Use _PyRuntimeState_GetFinalizing() and _PyRuntimeState_SetFinalizing()
+       to access it, don't access it directly. */
+    PyThreadState *_finalizing;
+    /* The ID of the OS thread in which we are finalizing. */
+    unsigned long _finalizing_id;
+
+    struct pyinterpreters {
+        PyMutex mutex;
+        /* The linked list of interpreters, newest first. */
+        PyInterpreterState *head;
+        /* The runtime's initial interpreter, which has a special role
+           in the operation of the runtime.  It is also often the only
+           interpreter. */
+        PyInterpreterState *main;
+        /* next_id is an auto-numbered sequence of small
+           integers.  It gets initialized in _PyInterpreterState_Enable(),
+           which is called in Py_Initialize(), and used in
+           PyInterpreterState_New().  A negative interpreter ID
+           indicates an error occurred.  The main interpreter will
+           always have an ID of 0.  Overflow results in a RuntimeError.
+           If that becomes a problem later then we can adjust, e.g. by
+           using a Python int. */
+        int64_t next_id;
+    } interpreters;
+
+    /* Platform-specific identifier and PyThreadState, respectively, for the
+       main thread in the main interpreter. */
+    unsigned long main_thread;
+    PyThreadState *main_tstate;
+
+    /* ---------- IMPORTANT ---------------------------
+     The fields above this line are declared as early as
+     possible to facilitate out-of-process observability
+     tools. */
+
+    /* cross-interpreter data and utils */
+    _PyXI_global_state_t xi;
+
+    struct _pymem_allocators allocators;
+    struct _obmalloc_global_state obmalloc;
+    struct pyhash_runtime_state pyhash_state;
+    struct _pythread_runtime_state threads;
+    struct _signals_runtime_state signals;
+
+    /* Used for the thread state bound to the current thread. */
+    Py_tss_t autoTSSkey;
+
+    /* Used instead of PyThreadState.trash when there is not current tstate. */
+    Py_tss_t trashTSSkey;
+
+    PyWideStringList orig_argv;
+
+    struct _parser_runtime_state parser;
+
+    struct _atexit_runtime_state atexit;
+
+    struct _import_runtime_state imports;
+    struct _ceval_runtime_state ceval;
+    struct _gilstate_runtime_state {
+        /* bpo-26558: Flag to disable PyGILState_Check().
+           If set to non-zero, PyGILState_Check() always return 1. */
+        int check_enabled;
+        /* The single PyInterpreterState used by this process'
+           GILState implementation
+        */
+        /* TODO: Given interp_main, it may be possible to kill this ref */
+        PyInterpreterState *autoInterpreterState;
+    } gilstate;
+    struct _getargs_runtime_state {
+        struct _PyArg_Parser *static_parsers;
+    } getargs;
+    struct _fileutils_state fileutils;
+    struct _faulthandler_runtime_state faulthandler;
+    struct _tracemalloc_runtime_state tracemalloc;
+    struct _reftracer_runtime_state ref_tracer;
+
+    // The rwmutex is used to prevent overlapping global and per-interpreter
+    // stop-the-world events. Global stop-the-world events lock the mutex
+    // exclusively (as a "writer"), while per-interpreter stop-the-world events
+    // lock it non-exclusively (as "readers").
+    _PyRWMutex stoptheworld_mutex;
+    struct _stoptheworld_state stoptheworld;
+
+    PyPreConfig preconfig;
+
+    // Audit values must be preserved when Py_Initialize()/Py_Finalize()
+    // is called multiple times.
+    Py_OpenCodeHookFunction open_code_hook;
+    void *open_code_userdata;
+    struct {
+        PyMutex mutex;
+        struct _Py_AuditHookEntry *head;
+    } audit_hooks;
+
+    struct _py_object_runtime_state object_state;
+    struct _Py_float_runtime_state float_state;
+    struct _Py_unicode_runtime_state unicode_state;
+    struct _types_runtime_state types;
+    struct _Py_time_runtime_state time;
+
+#if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE)
+    // Used in "Python/emscripten_trampoline.c" to choose between type
+    // reflection trampoline and EM_JS trampoline.
+    int (*emscripten_count_args_function)(PyCFunctionWithKeywords func);
+#endif
+
+    /* All the objects that are shared by the runtime's interpreters. */
+    struct _Py_cached_objects cached_objects;
+    struct _Py_static_objects static_objects;
+
+    /* The following fields are here to avoid allocation during init.
+       The data is exposed through _PyRuntimeState pointer fields.
+       These fields should not be accessed directly outside of init.
+
+       All other _PyRuntimeState pointer fields are populated when
+       needed and default to NULL.
+
+       For now there are some exceptions to that rule, which require
+       allocation during init.  These will be addressed on a case-by-case
+       basis.  Most notably, we don't pre-allocated the several mutex
+       (PyThread_type_lock) fields, because on Windows we only ever get
+       a pointer type.
+       */
+
+    /* _PyRuntimeState.interpreters.main */
+    PyInterpreterState _main_interpreter;
+    // _main_interpreter should be the last field of _PyRuntimeState.
+    // See https://github.com/python/cpython/issues/127117.
+} _PyRuntimeState;
+
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* Py_INTERNAL_RUNTIME_STRUCTS_H */
diff --git a/Include/internal/pycore_stackref.h 
b/Include/internal/pycore_stackref.h
index 98ebf66946d726..398a1bdaf1cf81 100644
--- a/Include/internal/pycore_stackref.h
+++ b/Include/internal/pycore_stackref.h
@@ -4,9 +4,6 @@
 extern "C" {
 #endif
 
-// Define this to get precise tracking of stackrefs.
-// #define Py_STACKREF_DEBUG 1
-
 // Define this to get precise tracking of closed stackrefs.
 // This will use unbounded memory, as it can only grow.
 // Use this to track double closes in short-lived programs
@@ -60,10 +57,6 @@ extern "C" {
 
 #if !defined(Py_GIL_DISABLED) && defined(Py_STACKREF_DEBUG)
 
-typedef union _PyStackRef {
-    uint64_t index;
-} _PyStackRef;
-
 #define Py_TAG_BITS 0
 
 PyAPI_FUNC(PyObject *) _Py_stackref_get_object(_PyStackRef ref);
@@ -203,10 +196,6 @@ PyStackRef_IsHeapSafe(_PyStackRef ref)
 
 #else
 
-typedef union _PyStackRef {
-    uintptr_t bits;
-} _PyStackRef;
-
 
 #ifdef Py_GIL_DISABLED
 
diff --git a/Include/internal/pycore_structs.h 
b/Include/internal/pycore_structs.h
new file mode 100644
index 00000000000000..ed0e987c23e851
--- /dev/null
+++ b/Include/internal/pycore_structs.h
@@ -0,0 +1,74 @@
+#ifndef Py_INTERNAL_STRUCTS_H
+#define Py_INTERNAL_STRUCTS_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* This files contains various key structs that are widely used
+ * and do not depend on other headers. */
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+
+typedef struct {
+    uint16_t value_and_backoff;
+} _Py_BackoffCounter;
+
+/* Each instruction in a code object is a fixed-width value,
+ * currently 2 bytes: 1-byte opcode + 1-byte oparg.  The EXTENDED_ARG
+ * opcode allows for larger values but the current limit is 3 uses
+ * of EXTENDED_ARG (see Python/compile.c), for a maximum
+ * 32-bit value.  This aligns with the note in Python/compile.c
+ * (compiler_addop_i_line) indicating that the max oparg value is
+ * 2**32 - 1, rather than INT_MAX.
+ */
+typedef union {
+    uint16_t cache;
+    struct {
+        uint8_t code;
+        uint8_t arg;
+    } op;
+    _Py_BackoffCounter counter;  // First cache entry of specializable op
+} _Py_CODEUNIT;
+
+
+/* Abstract tree node. */
+typedef struct {
+    PyObject_HEAD
+} PyHamtNode;
+
+
+/* An HAMT immutable mapping collection. */
+typedef struct {
+    PyObject_HEAD
+    PyHamtNode *h_root;
+    PyObject *h_weakreflist;
+    Py_ssize_t h_count;
+} PyHamtObject;
+
+typedef struct {
+    PyObject_VAR_HEAD
+    uint32_t b_bitmap;
+    PyObject *b_array[1];
+} PyHamtNode_Bitmap;
+
+#include "pycore_context.h"
+
+// Define this to get precise tracking of stackrefs.
+// #define Py_STACKREF_DEBUG 1
+
+typedef union _PyStackRef {
+#if !defined(Py_GIL_DISABLED) && defined(Py_STACKREF_DEBUG)
+    uint64_t index;
+#else
+    uintptr_t bits;
+#endif
+} _PyStackRef;
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* Py_INTERNAL_STRUCTS_H */
diff --git a/Include/internal/pycore_time.h b/Include/internal/pycore_time.h
index 833987fd639344..f50c43d8e552b5 100644
--- a/Include/internal/pycore_time.h
+++ b/Include/internal/pycore_time.h
@@ -57,6 +57,7 @@ extern "C" {
 #  error "this header requires Py_BUILD_CORE define"
 #endif
 
+#include "pycore_runtime_structs.h"
 
 #ifdef __clang__
 struct timeval;
@@ -307,11 +308,6 @@ PyAPI_FUNC(PyTime_t) _PyDeadline_Get(PyTime_t deadline);
 
 // --- _PyTimeFraction -------------------------------------------------------
 
-typedef struct {
-    PyTime_t numer;
-    PyTime_t denom;
-} _PyTimeFraction;
-
 // Set a fraction.
 // Return 0 on success.
 // Return -1 if the fraction is invalid.
@@ -330,17 +326,6 @@ extern PyTime_t _PyTimeFraction_Mul(
 extern double _PyTimeFraction_Resolution(
     const _PyTimeFraction *frac);
 
-
-// --- _Py_time_runtime_state ------------------------------------------------
-
-struct _Py_time_runtime_state {
-#if defined(MS_WINDOWS) || defined(__APPLE__)
-    _PyTimeFraction base;
-#else
-    char _unused;
-#endif
-};
-
 extern PyStatus _PyTime_Init(struct _Py_time_runtime_state *state);
 
 #ifdef __cplusplus
diff --git a/Include/internal/pycore_typeobject.h 
b/Include/internal/pycore_typeobject.h
index 581153344a8e05..b5e72b7c38ffff 100644
--- a/Include/internal/pycore_typeobject.h
+++ b/Include/internal/pycore_typeobject.h
@@ -10,6 +10,7 @@ extern "C" {
 
 #include "pycore_moduleobject.h"  // PyModuleObject
 #include "pycore_lock.h"          // PyMutex
+#include "pycore_runtime_structs.h" // type state
 
 
 /* state */
@@ -32,126 +33,6 @@ extern "C" {
 #define _Py_TYPE_BASE_VERSION_TAG (2<<16)
 #define _Py_MAX_GLOBAL_TYPE_VERSION_TAG (_Py_TYPE_BASE_VERSION_TAG - 1)
 
-/* For now we hard-code this to a value for which we are confident
-   all the static builtin types will fit (for all builds). */
-#define _Py_MAX_MANAGED_STATIC_BUILTIN_TYPES 200
-#define _Py_MAX_MANAGED_STATIC_EXT_TYPES 10
-#define _Py_MAX_MANAGED_STATIC_TYPES \
-    (_Py_MAX_MANAGED_STATIC_BUILTIN_TYPES + _Py_MAX_MANAGED_STATIC_EXT_TYPES)
-
-struct _types_runtime_state {
-    /* Used to set PyTypeObject.tp_version_tag for core static types. */
-    // bpo-42745: next_version_tag remains shared by all interpreters
-    // because of static types.
-    unsigned int next_version_tag;
-
-    struct {
-        struct {
-            PyTypeObject *type;
-            int64_t interp_count;
-        } types[_Py_MAX_MANAGED_STATIC_TYPES];
-    } managed_static;
-};
-
-
-// Type attribute lookup cache: speed up attribute and method lookups,
-// see _PyType_Lookup().
-struct type_cache_entry {
-    unsigned int version;  // initialized from type->tp_version_tag
-#ifdef Py_GIL_DISABLED
-   _PySeqLock sequence;
-#endif
-    PyObject *name;        // reference to exactly a str or None
-    PyObject *value;       // borrowed reference or NULL
-};
-
-#define MCACHE_SIZE_EXP 12
-
-struct type_cache {
-    struct type_cache_entry hashtable[1 << MCACHE_SIZE_EXP];
-};
-
-typedef struct {
-    PyTypeObject *type;
-    int isbuiltin;
-    int readying;
-    int ready;
-    // XXX tp_dict can probably be statically allocated,
-    // instead of dynamically and stored on the interpreter.
-    PyObject *tp_dict;
-    PyObject *tp_subclasses;
-    /* We never clean up weakrefs for static builtin types since
-       they will effectively never get triggered.  However, there
-       are also some diagnostic uses for the list of weakrefs,
-       so we still keep it. */
-    PyObject *tp_weaklist;
-} managed_static_type_state;
-
-#define TYPE_VERSION_CACHE_SIZE (1<<12)  /* Must be a power of 2 */
-
-struct types_state {
-    /* Used to set PyTypeObject.tp_version_tag.
-       It starts at _Py_MAX_GLOBAL_TYPE_VERSION_TAG + 1,
-       where all those lower numbers are used for core static types. */
-    unsigned int next_version_tag;
-
-    struct type_cache type_cache;
-
-    /* Every static builtin type is initialized for each interpreter
-       during its own initialization, including for the main interpreter
-       during global runtime initialization.  This is done by calling
-       _PyStaticType_InitBuiltin().
-
-       The first time a static builtin type is initialized, all the
-       normal PyType_Ready() stuff happens.  The only difference from
-       normal is that there are three PyTypeObject fields holding
-       objects which are stored here (on PyInterpreterState) rather
-       than in the corresponding PyTypeObject fields.  Those are:
-       tp_dict (cls.__dict__), tp_subclasses (cls.__subclasses__),
-       and tp_weaklist.
-
-       When a subinterpreter is initialized, each static builtin type
-       is still initialized, but only the interpreter-specific portion,
-       namely those three objects.
-
-       Those objects are stored in the PyInterpreterState.types.builtins
-       array, at the index corresponding to each specific static builtin
-       type.  That index (a size_t value) is stored in the tp_subclasses
-       field.  For static builtin types, we re-purposed the now-unused
-       tp_subclasses to avoid adding another field to PyTypeObject.
-       In all other cases tp_subclasses holds a dict like before.
-       (The field was previously defined as PyObject*, but is now void*
-       to reflect its dual use.)
-
-       The index for each static builtin type isn't statically assigned.
-       Instead it is calculated the first time a type is initialized
-       (by the main interpreter).  The index matches the order in which
-       the type was initialized relative to the others.  The actual
-       value comes from the current value of num_builtins_initialized,
-       as each type is initialized for the main interpreter.
-
-       num_builtins_initialized is incremented once for each static
-       builtin type.  Once initialization is over for a subinterpreter,
-       the value will be the same as for all other interpreters.  */
-    struct {
-        size_t num_initialized;
-        managed_static_type_state 
initialized[_Py_MAX_MANAGED_STATIC_BUILTIN_TYPES];
-    } builtins;
-    /* We apply a similar strategy for managed extension modules. */
-    struct {
-        size_t num_initialized;
-        size_t next_index;
-        managed_static_type_state 
initialized[_Py_MAX_MANAGED_STATIC_EXT_TYPES];
-    } for_extensions;
-    PyMutex mutex;
-
-    // Borrowed references to type objects whose
-    // tp_version_tag % TYPE_VERSION_CACHE_SIZE
-    // once was equal to the index in the table.
-    // They are cleared when the type object is deallocated.
-    PyTypeObject *type_version_cache[TYPE_VERSION_CACHE_SIZE];
-};
-
 
 /* runtime lifecycle */
 
@@ -161,17 +42,6 @@ extern void _PyTypes_FiniExtTypes(PyInterpreterState 
*interp);
 extern void _PyTypes_Fini(PyInterpreterState *);
 extern void _PyTypes_AfterFork(void);
 
-/* other API */
-
-/* Length of array of slotdef pointers used to store slots with the
-   same __name__.  There should be at most MAX_EQUIV-1 slotdef entries with
-   the same __name__, for any __name__. Since that's a static property, it is
-   appropriate to declare fixed-size arrays for this. */
-#define MAX_EQUIV 10
-
-typedef struct wrapperbase pytype_slotdef;
-
-
 static inline PyObject **
 _PyStaticType_GET_WEAKREFS_LISTPTR(managed_static_type_state *state)
 {
diff --git a/Include/internal/pycore_unicodeobject.h 
b/Include/internal/pycore_unicodeobject.h
index 13c3213132568b..c22be338df48a3 100644
--- a/Include/internal/pycore_unicodeobject.h
+++ b/Include/internal/pycore_unicodeobject.h
@@ -287,40 +287,6 @@ extern void _PyUnicode_InternStatic(PyInterpreterState 
*interp, PyObject **);
 
 /* --- Other API ---------------------------------------------------------- */
 
-struct _Py_unicode_runtime_ids {
-    PyMutex mutex;
-    // next_index value must be preserved when Py_Initialize()/Py_Finalize()
-    // is called multiple times: see _PyUnicode_FromId() implementation.
-    Py_ssize_t next_index;
-};
-
-struct _Py_unicode_runtime_state {
-    struct _Py_unicode_runtime_ids ids;
-};
-
-/* fs_codec.encoding is initialized to NULL.
-   Later, it is set to a non-NULL string by _PyUnicode_InitEncodings(). */
-struct _Py_unicode_fs_codec {
-    char *encoding;   // Filesystem encoding (encoded to UTF-8)
-    int utf8;         // encoding=="utf-8"?
-    char *errors;     // Filesystem errors (encoded to UTF-8)
-    _Py_error_handler error_handler;
-};
-
-struct _Py_unicode_ids {
-    Py_ssize_t size;
-    PyObject **array;
-};
-
-struct _Py_unicode_state {
-    struct _Py_unicode_fs_codec fs_codec;
-
-    _PyUnicode_Name_CAPI *ucnhash_capi;
-
-    // Unicode identifiers (_Py_Identifier): see _PyUnicode_FromId()
-    struct _Py_unicode_ids ids;
-};
-
 extern void _PyUnicode_ClearInterned(PyInterpreterState *interp);
 
 // Like PyUnicode_AsUTF8(), but check for embedded null characters.
diff --git a/Include/internal/pycore_uniqueid.h 
b/Include/internal/pycore_uniqueid.h
index 9d3c866a704894..c80924d50e677a 100644
--- a/Include/internal/pycore_uniqueid.h
+++ b/Include/internal/pycore_uniqueid.h
@@ -24,26 +24,6 @@ extern "C" {
 // Each entry implicitly represents a unique id based on its offset in the
 // table. Non-allocated entries form a free-list via the 'next' pointer.
 // Allocated entries store the corresponding PyObject.
-typedef union _Py_unique_id_entry {
-    // Points to the next free type id, when part of the freelist
-    union _Py_unique_id_entry *next;
-
-    // Stores the object when the id is assigned
-    PyObject *obj;
-} _Py_unique_id_entry;
-
-struct _Py_unique_id_pool {
-    PyMutex mutex;
-
-    // combined table of object with allocated unique ids and unallocated ids.
-    _Py_unique_id_entry *table;
-
-    // Next entry to allocate inside 'table' or NULL
-    _Py_unique_id_entry *freelist;
-
-    // size of 'table'
-    Py_ssize_t size;
-};
 
 #define _Py_INVALID_UNIQUE_ID 0
 
diff --git a/Include/internal/pycore_warnings.h 
b/Include/internal/pycore_warnings.h
index 672228cd6fbd19..e1348d529a2246 100644
--- a/Include/internal/pycore_warnings.h
+++ b/Include/internal/pycore_warnings.h
@@ -8,16 +8,6 @@ extern "C" {
 #  error "this header requires Py_BUILD_CORE define"
 #endif
 
-struct _warnings_runtime_state {
-    /* Both 'filters' and 'onceregistry' can be set in warnings.py;
-       get_warnings_attr() will reset these variables accordingly. */
-    PyObject *filters;  /* List */
-    PyObject *once_registry;  /* Dict */
-    PyObject *default_action; /* String */
-    _PyRecursiveMutex lock;
-    long filters_version;
-};
-
 extern int _PyWarnings_InitState(PyInterpreterState *interp);
 
 extern PyObject* _PyWarnings_Init(void);
diff --git a/Makefile.pre.in b/Makefile.pre.in
index 646096054a7b66..a65854ac125c60 100644
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -1255,6 +1255,7 @@ PYTHON_HEADERS= \
                $(srcdir)/Include/internal/pycore_instruments.h \
                $(srcdir)/Include/internal/pycore_instruction_sequence.h \
                $(srcdir)/Include/internal/pycore_interp.h \
+               $(srcdir)/Include/internal/pycore_interp_structs.h \
                $(srcdir)/Include/internal/pycore_intrinsics.h \
                $(srcdir)/Include/internal/pycore_jit.h \
                $(srcdir)/Include/internal/pycore_list.h \
@@ -1297,11 +1298,13 @@ PYTHON_HEADERS= \
                $(srcdir)/Include/internal/pycore_runtime.h \
                $(srcdir)/Include/internal/pycore_runtime_init.h \
                $(srcdir)/Include/internal/pycore_runtime_init_generated.h \
+               $(srcdir)/Include/internal/pycore_runtime_structs.h \
                $(srcdir)/Include/internal/pycore_semaphore.h \
                $(srcdir)/Include/internal/pycore_setobject.h \
                $(srcdir)/Include/internal/pycore_signal.h \
                $(srcdir)/Include/internal/pycore_sliceobject.h \
                $(srcdir)/Include/internal/pycore_strhex.h \
+               $(srcdir)/Include/internal/pycore_structs.h \
                $(srcdir)/Include/internal/pycore_structseq.h \
                $(srcdir)/Include/internal/pycore_symtable.h \
                $(srcdir)/Include/internal/pycore_sysmodule.h \
diff --git a/Modules/_codecsmodule.c b/Modules/_codecsmodule.c
index 471b42badc8e8c..7cf3f152eeecc6 100644
--- a/Modules/_codecsmodule.c
+++ b/Modules/_codecsmodule.c
@@ -32,6 +32,7 @@ Copyright (c) Corporation for National Research Initiatives.
 
 #include "Python.h"
 #include "pycore_codecs.h"        // _PyCodec_Lookup()
+#include "pycore_unicodeobject.h" // _PyUnicode_EncodeCharmap
 
 #ifdef MS_WINDOWS
 #include <windows.h>
diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c
index 2c88bd3cdf2347..2025724953969b 100644
--- a/Modules/_cursesmodule.c
+++ b/Modules/_cursesmodule.c
@@ -109,6 +109,7 @@ static const char PyCursesVersion[] = "2.2";
 #include "pycore_long.h"        // _PyLong_GetZero()
 #include "pycore_structseq.h"   // _PyStructSequence_NewType()
 #include "pycore_sysmodule.h"   // _PySys_GetOptionalAttrString()
+#include "pycore_fileutils.h"   // _Py_set_inheritable
 
 #ifdef __hpux
 #define STRICT_SYSV_CURSES
diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c
index c7cc3a8071dc1c..77695401919cb7 100644
--- a/Modules/_tkinter.c
+++ b/Modules/_tkinter.c
@@ -32,6 +32,7 @@ Copyright (C) 1994 Steen Lumholt.
 
 #include "pycore_long.h"          // _PyLong_IsNegative()
 #include "pycore_sysmodule.h"     // _PySys_GetOptionalAttrString()
+#include "pycore_unicodeobject.h" // _PyUnicode_AsUTF8String
 
 #ifdef MS_WINDOWS
 #  include <windows.h>
diff --git a/Objects/genobject.c b/Objects/genobject.c
index 2f70df79a3d3b0..9decca5048f5cf 100644
--- a/Objects/genobject.c
+++ b/Objects/genobject.c
@@ -3,6 +3,8 @@
 #define _PY_INTERPRETER
 
 #include "Python.h"
+#include "pycore_genobject.h"
+
 #include "pycore_call.h"          // _PyObject_CallNoArgs()
 #include "pycore_ceval.h"         // _PyEval_EvalFrame()
 #include "pycore_frame.h"         // _PyInterpreterFrame
diff --git a/Objects/listobject.c b/Objects/listobject.c
index 85eb5e56853b8f..95656686e461ac 100644
--- a/Objects/listobject.c
+++ b/Objects/listobject.c
@@ -13,6 +13,7 @@
 #include "pycore_modsupport.h"    // _PyArg_NoKwnames()
 #include "pycore_object.h"        // _PyObject_GC_TRACK(), 
_PyDebugAllocatorStats()
 #include "pycore_tuple.h"         // _PyTuple_FromArray()
+#include "pycore_typeobject.h"    // _Py_TYPE_VERSION_LIST
 #include "pycore_setobject.h"     // _PySet_NextEntry()
 #include <stddef.h>
 
diff --git a/Objects/object.c b/Objects/object.c
index 58aec5f2b5b3e1..c8f3831d6f3b82 100644
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -14,6 +14,7 @@
 #include "pycore_initconfig.h"    // _PyStatus_EXCEPTION()
 #include "pycore_instruction_sequence.h" // _PyInstructionSequence_Type
 #include "pycore_hashtable.h"     // _Py_hashtable_new()
+#include "pycore_hamt.h"          // _PyHamtItems_Type
 #include "pycore_memoryobject.h"  // _PyManagedBuffer_Type
 #include "pycore_namespace.h"     // _PyNamespace_Type
 #include "pycore_object.h"        // PyAPI_DATA() _Py_SwappedOp definition
diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c
index 085944cd6bd989..7a8ac322197b0e 100644
--- a/Objects/unicodeobject.c
+++ b/Objects/unicodeobject.c
@@ -53,6 +53,7 @@ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 
SOFTWARE.
 #include "pycore_object.h"        // _PyObject_GC_TRACK(), 
_Py_FatalRefcountError()
 #include "pycore_pathconfig.h"    // _Py_DumpPathConfig()
 #include "pycore_pyerrors.h"      // _PyUnicodeTranslateError_Create()
+#include "pycore_pyhash.h"        // _Py_HashSecret_t
 #include "pycore_pylifecycle.h"   // _Py_SetFileSystemEncoding()
 #include "pycore_pystate.h"       // _PyInterpreterState_GET()
 #include "pycore_ucnhash.h"       // _PyUnicode_Name_CAPI
diff --git a/Parser/pegen.c b/Parser/pegen.c
index be1768d0f2cff7..87a9ba02274d9f 100644
--- a/Parser/pegen.c
+++ b/Parser/pegen.c
@@ -1,7 +1,10 @@
 #include <Python.h>
 #include "pycore_ast.h"           // _PyAST_Validate(),
 #include "pycore_pystate.h"       // _PyThreadState_GET()
+#include "pycore_parser.h"        // _PYPEGEN_NSTATISTICS
 #include "pycore_pyerrors.h"      // PyExc_IncompleteInputError
+#include "pycore_runtime.h"     // _PyRuntime
+#include "pycore_unicodeobject.h" // _PyUnicode_InternImmortal
 #include <errcode.h>
 
 #include "lexer/lexer.h"
diff --git a/Python/bootstrap_hash.c b/Python/bootstrap_hash.c
index 1dc5bffe1b0003..f0fb87c4a5d15e 100644
--- a/Python/bootstrap_hash.c
+++ b/Python/bootstrap_hash.c
@@ -1,6 +1,7 @@
 #include "Python.h"
 #include "pycore_fileutils.h"     // _Py_fstat_noraise()
 #include "pycore_initconfig.h"
+#include "pycore_pyhash.h"        // _Py_HashSecret_t
 #include "pycore_pylifecycle.h"   // _PyOS_URandomNonblock()
 #include "pycore_runtime.h"       // _PyRuntime
 
diff --git a/Python/dtoa.c b/Python/dtoa.c
index d0c89b2b468f75..61d776d9a20687 100644
--- a/Python/dtoa.c
+++ b/Python/dtoa.c
@@ -122,6 +122,7 @@
 #include "pycore_pystate.h"       // _PyInterpreterState_GET()
 #include <stdlib.h>               // exit()
 
+
 /* if _PY_SHORT_FLOAT_REPR == 0, then don't even try to compile
    the following code */
 #if _PY_SHORT_FLOAT_REPR == 1
@@ -157,7 +158,7 @@
 #endif
 
 
-// ULong is defined in pycore_dtoa.h.
+typedef uint32_t ULong;
 typedef int32_t Long;
 typedef uint64_t ULLong;
 
diff --git a/Python/formatter_unicode.c b/Python/formatter_unicode.c
index dcfff2f5bc7d07..30807f428c7d71 100644
--- a/Python/formatter_unicode.c
+++ b/Python/formatter_unicode.c
@@ -5,6 +5,7 @@
 #include "Python.h"
 #include "pycore_fileutils.h"     // _Py_GetLocaleconvNumeric()
 #include "pycore_long.h"          // _PyLong_FormatWriter()
+#include "pycore_unicodeobject.h" // PyUnicode_MAX_CHAR_VALUE()
 #include <locale.h>
 
 /* Raises an exception about an unknown presentation type for this
diff --git a/Python/initconfig.c b/Python/initconfig.c
index 1fe92a021312fa..f73fbe76a96507 100644
--- a/Python/initconfig.c
+++ b/Python/initconfig.c
@@ -8,6 +8,7 @@
 #include "pycore_pyerrors.h"      // _PyErr_GetRaisedException()
 #include "pycore_pylifecycle.h"   // _Py_PreInitializeFromConfig()
 #include "pycore_pymem.h"         // _PyMem_DefaultRawMalloc()
+#include "pycore_pyhash.h"        // _Py_HashSecret
 #include "pycore_pystate.h"       // _PyThreadState_GET()
 #include "pycore_pystats.h"       // _Py_StatsOn()
 #include "pycore_sysmodule.h"     // _PySys_SetIntMaxStrDigits()
diff --git a/Python/instrumentation.c b/Python/instrumentation.c
index c180f4c55c9e1a..299066d9c206b6 100644
--- a/Python/instrumentation.c
+++ b/Python/instrumentation.c
@@ -18,6 +18,7 @@
 #include "pycore_pyatomic_ft_wrappers.h" // FT_ATOMIC_STORE_UINTPTR_RELEASE
 #include "pycore_pyerrors.h"
 #include "pycore_pystate.h"       // _PyInterpreterState_GET()
+#include "pycore_runtime_structs.h"     // _PyCoMonitoringData
 
 /* Uncomment this to dump debugging output when assertions fail */
 // #define INSTRUMENT_DEBUG 1
diff --git a/Python/interpconfig.c b/Python/interpconfig.c
index 54e5dca284c215..1add8a81425b9a 100644
--- a/Python/interpconfig.c
+++ b/Python/interpconfig.c
@@ -1,6 +1,7 @@
 /* PyInterpreterConfig API */
 
 #include "Python.h"
+#include "pycore_interp.h" // Py_RTFLAGS
 #include "pycore_pylifecycle.h"
 
 #include <stdbool.h>
diff --git a/Python/intrinsics.c b/Python/intrinsics.c
index 275e28f032e3b3..f6dfee3e9ab951 100644
--- a/Python/intrinsics.c
+++ b/Python/intrinsics.c
@@ -5,12 +5,15 @@
 #include "pycore_frame.h"
 #include "pycore_function.h"
 #include "pycore_global_objects.h"
+#include "pycore_genobject.h"     // _PyAsyncGenValueWrapperNew
 #include "pycore_compile.h"       // _PyCompile_GetUnaryIntrinsicName, etc
 #include "pycore_intrinsics.h"    // INTRINSIC_PRINT
 #include "pycore_pyerrors.h"      // _PyErr_SetString()
 #include "pycore_runtime.h"       // _Py_ID()
 #include "pycore_sysmodule.h"     // _PySys_GetRequiredAttr()
+#include "pycore_tuple.h"         // _PyTuple_FromArray()
 #include "pycore_typevarobject.h" // _Py_make_typevar()
+#include "pycore_unicodeobject.h" // _PyUnicode_FromASCII
 
 
 /******** Unary functions ********/
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index 4007e43c98d640..0513d614c6e1ef 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -1,7 +1,8 @@
 /* Python interpreter top-level routines, including init/exit */
 
 #include "Python.h"
-
+#include "pycore_runtime_structs.h"
+#include "pycore_unicodeobject.h"
 #include "pycore_audit.h"         // _PySys_ClearAuditHooks()
 #include "pycore_call.h"          // _PyObject_CallMethod()
 #include "pycore_ceval.h"         // _PyEval_FiniGIL()
@@ -13,6 +14,7 @@
 #include "pycore_freelist.h"      // _PyObject_ClearFreeLists()
 #include "pycore_floatobject.h"   // _PyFloat_InitTypes()
 #include "pycore_global_objects_fini_generated.h"  // 
"_PyStaticObjects_CheckRefcnt()
+#include "pycore_hamt.h"          // _PyHamt_Type
 #include "pycore_import.h"        // _PyImport_BootstrapImp()
 #include "pycore_initconfig.h"    // _PyStatus_OK()
 #include "pycore_list.h"          // _PyList_Fini()
diff --git a/Python/pystate.c b/Python/pystate.c
index fcd12d1b933360..99f8774d446986 100644
--- a/Python/pystate.c
+++ b/Python/pystate.c
@@ -2,6 +2,8 @@
 /* Thread and interpreter state structures and their interfaces */
 
 #include "Python.h"
+#include "pycore_runtime_structs.h"
+
 #include "pycore_abstract.h"      // _PyIndex_Check()
 #include "pycore_audit.h"         // _Py_AuditHookEntry
 #include "pycore_ceval.h"
@@ -18,6 +20,7 @@
 #include "pycore_pylifecycle.h"   // _PyAST_Fini()
 #include "pycore_pymem.h"         // _PyMem_DebugEnabled()
 #include "pycore_pystate.h"
+#include "pycore_runtime.h"       // _PyRuntime
 #include "pycore_runtime_init.h"  // _PyRuntimeState_INIT
 #include "pycore_stackref.h"      // Py_STACKREF_DEBUG
 #include "pycore_time.h"          // _PyTime_Init()
diff --git a/Python/suggestions.c b/Python/suggestions.c
index 2ce0bcfd54e23f..154a8ade1b0153 100644
--- a/Python/suggestions.c
+++ b/Python/suggestions.c
@@ -3,6 +3,7 @@
 #include "pycore_frame.h"
 #include "pycore_pyerrors.h"      // export _Py_UTF8_Edit_Cost()
 #include "pycore_runtime.h"       // _Py_ID()
+#include "pycore_unicodeobject.h" // _PyUnicode_Equal()
 
 #define MAX_CANDIDATE_ITEMS 750
 #define MAX_STRING_SIZE 40
diff --git a/Tools/build/generate_global_objects.py 
b/Tools/build/generate_global_objects.py
index b5b6de0e7dc2dc..219fea771d53a6 100644
--- a/Tools/build/generate_global_objects.py
+++ b/Tools/build/generate_global_objects.py
@@ -277,7 +277,7 @@ def generate_runtime_init(identifiers, strings):
     # First get some info from the declarations.
     nsmallposints = None
     nsmallnegints = None
-    with open(os.path.join(INTERNAL, 'pycore_global_objects.h')) as infile:
+    with open(os.path.join(INTERNAL, 'pycore_runtime_structs.h')) as infile:
         for line in infile:
             if line.startswith('#define _PY_NSMALLPOSINTS'):
                 nsmallposints = int(line.split()[-1])

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com

Reply via email to