44extern "C" {
55#endif
66
7+ /* Legacy Opcache */
78
89typedef struct {
910 PyObject * ptr ; /* Cached pointer (borrowed reference) */
@@ -26,6 +27,129 @@ struct _PyOpcache {
2627};
2728
2829
30+ /* PEP 659
31+ * Specialization and quickening structs and helper functions
32+ */
33+
34+ typedef struct {
35+ int32_t cache_count ;
36+ int32_t _ ; /* Force 8 byte size */
37+ } _PyEntryZero ;
38+
39+ typedef struct {
40+ uint8_t original_oparg ;
41+ uint8_t counter ;
42+ uint16_t index ;
43+ } _PyAdaptiveEntry ;
44+
45+ /* Add specialized versions of entries to this union.
46+ *
47+ * Do not break the invariant: sizeof(SpecializedCacheEntry) == 8
48+ * Preserving this invariant is necessary because:
49+ - If any one form uses more space, then all must and on 64 bit machines
50+ this is likely to double the memory consumption of caches
51+ - The function for calculating the offset of caches assumes a 4:1
52+ cache:instruction size ratio. Changing that would need careful
53+ analysis to choose a new function.
54+ */
55+ typedef union {
56+ _PyEntryZero zero ;
57+ _PyAdaptiveEntry adaptive ;
58+ } SpecializedCacheEntry ;
59+
60+ #define INSTRUCTIONS_PER_ENTRY (sizeof(SpecializedCacheEntry)/sizeof(_Py_CODEUNIT))
61+
62+ /* Maximum size of code to quicken, in code units. */
63+ #define MAX_SIZE_TO_QUICKEN 5000
64+
65+ typedef union _cache_or_instruction {
66+ _Py_CODEUNIT code [1 ];
67+ SpecializedCacheEntry entry ;
68+ } SpecializedCacheOrInstruction ;
69+
70+ /* Get pointer to the nth cache entry, from the first instruction and n.
71+ * Cache entries are indexed backwards, with [count-1] first in memory, and [0] last.
72+ * The zeroth entry immediately precedes the instructions.
73+ */
74+ static inline SpecializedCacheEntry *
75+ _GetSpecializedCacheEntry (_Py_CODEUNIT * first_instr , Py_ssize_t n )
76+ {
77+ SpecializedCacheOrInstruction * last_cache_plus_one = (SpecializedCacheOrInstruction * )first_instr ;
78+ assert (& last_cache_plus_one -> code [0 ] == first_instr );
79+ return & last_cache_plus_one [-1 - n ].entry ;
80+ }
81+
82+ /* Following two functions form a pair.
83+ *
84+ * oparg_from_offset_and_index() is used to compute the oparg
85+ * when quickening, so that offset_from_oparg_and_nexti()
86+ * can be used at runtime to compute the offset.
87+ *
88+ * The relationship between the three values is currently
89+ * offset == (index>>1) + oparg
90+ * This relation is chosen based on the following observations:
91+ * 1. typically 1 in 4 instructions need a cache
92+ * 2. instructions that need a cache typically use 2 entries
93+ * These observations imply: offset ≈ index/2
94+ * We use the oparg to fine tune the relation to avoid wasting space
95+ * and allow consecutive instructions to use caches.
96+ *
97+ * If the number of cache entries < number of instructions/2 we will waste
98+ * some small amoount of space.
99+ * If the number of cache entries > (number of instructions/2) + 255, then
100+ * some instructions will not be able to use a cache.
101+ * In practice, we expect some small amount of wasted space in a shorter functions
102+ * and only functions exceeding a 1000 lines or more not to have enugh cache space.
103+ *
104+ */
105+ static inline int
106+ oparg_from_offset_and_nexti (int offset , int nexti )
107+ {
108+ return offset - (nexti >>1 );
109+ }
110+
111+ static inline int
112+ offset_from_oparg_and_nexti (int oparg , int nexti )
113+ {
114+ return (nexti >>1 )+ oparg ;
115+ }
116+
117+ /* Get pointer to the cache entry associated with an instruction.
118+ * nexti is the index of the instruction plus one.
119+ * nexti is used as it corresponds to the instruction pointer in the interpreter.
120+ * This doesn't check that an entry has been allocated for that instruction. */
121+ static inline SpecializedCacheEntry *
122+ _GetSpecializedCacheEntryForInstruction (_Py_CODEUNIT * first_instr , int nexti , int oparg )
123+ {
124+ return _GetSpecializedCacheEntry (
125+ first_instr ,
126+ offset_from_oparg_and_nexti (oparg , nexti )
127+ );
128+ }
129+
130+ #define QUICKENING_WARMUP_DELAY 8
131+
132+ /* We want to compare to zero for efficiency, so we offset values accordingly */
133+ #define QUICKENING_INITIAL_WARMUP_VALUE (-QUICKENING_WARMUP_DELAY)
134+ #define QUICKENING_WARMUP_COLDEST 1
135+
136+ static inline void
137+ PyCodeObject_IncrementWarmup (PyCodeObject * co )
138+ {
139+ co -> co_warmup ++ ;
140+ }
141+
142+ /* Used by the interpreter to determine when a code object should be quickened */
143+ static inline int
144+ PyCodeObject_IsWarmedUp (PyCodeObject * co )
145+ {
146+ return (co -> co_warmup == 0 );
147+ }
148+
149+ int _Py_Quicken (PyCodeObject * code );
150+
151+ extern Py_ssize_t _Py_QuickenedCount ;
152+
29153struct _PyCodeConstructor {
30154 /* metadata */
31155 PyObject * filename ;
0 commit comments