Coverage Report

Created: 2026-05-30 06:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cpython/Python/ceval_macros.h
Line
Count
Source
1
// Macros and other things needed by ceval.c, and bytecodes.c
2
3
/* Computed GOTOs, or
4
       the-optimization-commonly-but-improperly-known-as-"threaded code"
5
   using gcc's labels-as-values extension
6
   (http://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html).
7
8
   The traditional bytecode evaluation loop uses a "switch" statement, which
9
   decent compilers will optimize as a single indirect branch instruction
10
   combined with a lookup table of jump addresses. However, since the
11
   indirect jump instruction is shared by all opcodes, the CPU will have a
12
   hard time making the right prediction for where to jump next (actually,
13
   it will be always wrong except in the uncommon case of a sequence of
14
   several identical opcodes).
15
16
   "Threaded code" in contrast, uses an explicit jump table and an explicit
17
   indirect jump instruction at the end of each opcode. Since the jump
18
   instruction is at a different address for each opcode, the CPU will make a
19
   separate prediction for each of these instructions, which is equivalent to
20
   predicting the second opcode of each opcode pair. These predictions have
21
   a much better chance to turn out valid, especially in small bytecode loops.
22
23
   A mispredicted branch on a modern CPU flushes the whole pipeline and
24
   can cost several CPU cycles (depending on the pipeline depth),
25
   and potentially many more instructions (depending on the pipeline width).
26
   A correctly predicted branch, however, is nearly free.
27
28
   At the time of this writing, the "threaded code" version is up to 15-20%
29
   faster than the normal "switch" version, depending on the compiler and the
30
   CPU architecture.
31
32
   NOTE: care must be taken that the compiler doesn't try to "optimize" the
33
   indirect jumps by sharing them between all opcodes. Such optimizations
34
   can be disabled on gcc by using the -fno-gcse flag (or possibly
35
   -fno-crossjumping).
36
*/
37
38
/* Use macros rather than inline functions, to make it as clear as possible
39
 * to the C compiler that the tracing check is a simple test then branch.
40
 * We want to be sure that the compiler knows this before it generates
41
 * the CFG.
42
 */
43
44
#ifdef WITH_DTRACE
45
#define OR_DTRACE_LINE | (PyDTrace_LINE_ENABLED() ? 255 : 0)
46
#else
47
#define OR_DTRACE_LINE
48
#endif
49
50
#ifdef HAVE_COMPUTED_GOTOS
51
    #ifndef USE_COMPUTED_GOTOS
52
    #define USE_COMPUTED_GOTOS 1
53
    #endif
54
#else
55
    #if defined(USE_COMPUTED_GOTOS) && USE_COMPUTED_GOTOS
56
    #error "Computed gotos are not supported on this compiler."
57
    #endif
58
    #undef USE_COMPUTED_GOTOS
59
    #define USE_COMPUTED_GOTOS 0
60
#endif
61
62
#ifdef Py_STATS
63
#define INSTRUCTION_STATS(op) \
64
    do { \
65
        PyStats *s = _PyStats_GET(); \
66
        OPCODE_EXE_INC(op); \
67
        if (s) s->opcode_stats[lastopcode].pair_count[op]++; \
68
        lastopcode = op; \
69
    } while (0)
70
#else
71
40.8G
#define INSTRUCTION_STATS(op) ((void)0)
72
#endif
73
74
#ifdef Py_STATS
75
#   define TAIL_CALL_PARAMS _PyInterpreterFrame *frame, _PyStackRef *stack_pointer, PyThreadState *tstate, _Py_CODEUNIT *next_instr, const void *instruction_funcptr_table, int oparg, int lastopcode
76
#   define TAIL_CALL_ARGS frame, stack_pointer, tstate, next_instr, instruction_funcptr_table, oparg, lastopcode
77
#else
78
#   define TAIL_CALL_PARAMS _PyInterpreterFrame *frame, _PyStackRef *stack_pointer, PyThreadState *tstate, _Py_CODEUNIT *next_instr, const void *instruction_funcptr_table, int oparg
79
#   define TAIL_CALL_ARGS frame, stack_pointer, tstate, next_instr, instruction_funcptr_table, oparg
80
#endif
81
82
#if _Py_TAIL_CALL_INTERP
83
#   if defined(__clang__) || defined(__GNUC__)
84
#       if !_Py__has_attribute(preserve_none) || !_Py__has_attribute(musttail)
85
#           error "This compiler does not have support for efficient tail calling."
86
#       endif
87
#   elif defined(_MSC_VER) && (_MSC_VER < 1950)
88
#       error "You need at least VS 2026 / PlatformToolset v145 for tail calling."
89
#   endif
90
#   if defined(_MSC_VER) && !defined(__clang__)
91
#      define Py_MUSTTAIL [[msvc::musttail]]
92
#      define Py_PRESERVE_NONE_CC __preserve_none
93
#   else
94
#       define Py_MUSTTAIL __attribute__((musttail))
95
#       define Py_PRESERVE_NONE_CC __attribute__((preserve_none))
96
#   endif
97
    typedef PyObject *(Py_PRESERVE_NONE_CC *py_tail_call_funcptr)(TAIL_CALL_PARAMS);
98
99
#   define DISPATCH_TABLE_VAR instruction_funcptr_table
100
#   define DISPATCH_TABLE instruction_funcptr_handler_table
101
#   define TRACING_DISPATCH_TABLE instruction_funcptr_tracing_table
102
#   define TARGET(op) Py_NO_INLINE PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_##op(TAIL_CALL_PARAMS)
103
104
#   define DISPATCH_GOTO() \
105
        do { \
106
            Py_MUSTTAIL return (((py_tail_call_funcptr *)instruction_funcptr_table)[opcode])(TAIL_CALL_ARGS); \
107
        } while (0)
108
#   define DISPATCH_GOTO_NON_TRACING() \
109
        do { \
110
            Py_MUSTTAIL return (((py_tail_call_funcptr *)DISPATCH_TABLE)[opcode])(TAIL_CALL_ARGS); \
111
        } while (0)
112
#   define JUMP_TO_LABEL(name) \
113
        do { \
114
            Py_MUSTTAIL return (_TAIL_CALL_##name)(TAIL_CALL_ARGS); \
115
        } while (0)
116
#   ifdef Py_STATS
117
#       define JUMP_TO_PREDICTED(name) \
118
            do { \
119
                Py_MUSTTAIL return (_TAIL_CALL_##name)(frame, stack_pointer, tstate, this_instr, instruction_funcptr_table, oparg, lastopcode); \
120
            } while (0)
121
#   else
122
#       define JUMP_TO_PREDICTED(name) \
123
            do { \
124
                Py_MUSTTAIL return (_TAIL_CALL_##name)(frame, stack_pointer, tstate, this_instr, instruction_funcptr_table, oparg); \
125
            } while (0)
126
#   endif
127
#    define LABEL(name) TARGET(name)
128
#elif USE_COMPUTED_GOTOS
129
#  define DISPATCH_TABLE_VAR opcode_targets
130
6.74M
#  define DISPATCH_TABLE opcode_targets_table
131
#  define TRACING_DISPATCH_TABLE opcode_tracing_targets_table
132
40.8G
#  define TARGET(op) TARGET_##op:
133
41.0G
#  define DISPATCH_GOTO() goto *opcode_targets[opcode]
134
6.74M
#  define DISPATCH_GOTO_NON_TRACING() goto *DISPATCH_TABLE[opcode];
135
154M
#  define JUMP_TO_LABEL(name) goto name;
136
257M
#  define JUMP_TO_PREDICTED(name) goto PREDICTED_##name;
137
479M
#  define LABEL(name) name:
138
#else
139
#  define TARGET(op) case op: TARGET_##op:
140
#  define DISPATCH_GOTO() dispatch_code = opcode | tracing_mode ; goto dispatch_opcode
141
#  define DISPATCH_GOTO_NON_TRACING() dispatch_code = opcode; goto dispatch_opcode
142
#  define JUMP_TO_LABEL(name) goto name;
143
#  define JUMP_TO_PREDICTED(name) goto PREDICTED_##name;
144
#  define LABEL(name) name:
145
#endif
146
147
#if (_Py_TAIL_CALL_INTERP || USE_COMPUTED_GOTOS) && _Py_TIER2
148
#  define IS_JIT_TRACING() (DISPATCH_TABLE_VAR == TRACING_DISPATCH_TABLE)
149
#  define ENTER_TRACING() \
150
    DISPATCH_TABLE_VAR = TRACING_DISPATCH_TABLE;
151
#  define LEAVE_TRACING() \
152
    DISPATCH_TABLE_VAR = DISPATCH_TABLE;
153
#else
154
#  define IS_JIT_TRACING() (tracing_mode != 0)
155
#  define ENTER_TRACING() tracing_mode = 255
156
#  define LEAVE_TRACING() tracing_mode = 0
157
#endif
158
159
#if _Py_TIER2
160
#define STOP_TRACING() \
161
    do { \
162
        if (IS_JIT_TRACING()) { \
163
            LEAVE_TRACING(); \
164
            _PyJit_FinalizeTracing(tstate, 0); \
165
        } \
166
    } while (0);
167
#else
168
133M
#define STOP_TRACING() ((void)(0));
169
#endif
170
171
/* PRE_DISPATCH_GOTO() does lltrace if enabled. Normally a no-op */
172
#ifdef Py_DEBUG
173
#define PRE_DISPATCH_GOTO() if (frame->lltrace >= 5) { \
174
    lltrace_instruction(frame, stack_pointer, next_instr, opcode, oparg); }
175
#else
176
41.0G
#define PRE_DISPATCH_GOTO() ((void)0)
177
#endif
178
179
#ifdef Py_DEBUG
180
#define LLTRACE_RESUME_FRAME() \
181
do { \
182
    _PyFrame_SetStackPointer(frame, stack_pointer); \
183
    int lltrace = maybe_lltrace_resume_frame(frame, GLOBALS()); \
184
    stack_pointer = _PyFrame_GetStackPointer(frame); \
185
    frame->lltrace = lltrace; \
186
} while (0)
187
#else
188
2.46G
#define LLTRACE_RESUME_FRAME() ((void)0)
189
#endif
190
191
#ifdef Py_GIL_DISABLED
192
#define QSBR_QUIESCENT_STATE(tstate) _Py_qsbr_quiescent_state(((_PyThreadStateImpl *)tstate)->qsbr)
193
#else
194
#define QSBR_QUIESCENT_STATE(tstate)
195
#endif
196
197
198
/* Do interpreter dispatch accounting for tracing and instrumentation */
199
#define DISPATCH() \
200
40.7G
    { \
201
40.7G
        assert(frame->stackpointer == NULL); \
202
40.7G
        NEXTOPARG(); \
203
40.7G
        PRE_DISPATCH_GOTO(); \
204
40.7G
        DISPATCH_GOTO(); \
205
40.7G
    }
206
207
#define DISPATCH_NON_TRACING() \
208
    { \
209
        assert(frame->stackpointer == NULL); \
210
        NEXTOPARG(); \
211
        PRE_DISPATCH_GOTO(); \
212
        DISPATCH_GOTO_NON_TRACING(); \
213
    }
214
215
#define DISPATCH_SAME_OPARG() \
216
6.74M
    { \
217
6.74M
        opcode = next_instr->op.code; \
218
6.74M
        PRE_DISPATCH_GOTO(); \
219
6.74M
        DISPATCH_GOTO_NON_TRACING(); \
220
6.74M
    }
221
222
#define DISPATCH_INLINED(NEW_FRAME)                              \
223
2.30M
    do {                                                         \
224
2.30M
        assert(!IS_PEP523_HOOKED(tstate));                       \
225
2.30M
        _PyFrame_SetStackPointer(frame, stack_pointer);          \
226
2.30M
        assert((NEW_FRAME)->previous == frame);                  \
227
2.30M
        frame = tstate->current_frame = (NEW_FRAME);             \
228
2.30M
        CALL_STAT_INC(inlined_py_calls);                         \
229
2.30M
        JUMP_TO_LABEL(start_frame);                              \
230
0
    } while (0)
231
232
/* Tuple access macros */
233
234
#ifndef Py_DEBUG
235
1.78G
#define GETITEM(v, i) PyTuple_GET_ITEM((v), (i))
236
#else
237
static inline PyObject *
238
GETITEM(PyObject *v, Py_ssize_t i) {
239
    assert(PyTuple_Check(v));
240
    assert(i >= 0);
241
    assert(i < PyTuple_GET_SIZE(v));
242
    return PyTuple_GET_ITEM(v, i);
243
}
244
#endif
245
246
/* Code access macros */
247
248
/* The integer overflow is checked by an assertion below. */
249
69.1M
#define INSTR_OFFSET() ((int)(next_instr - _PyFrame_GetBytecode(frame)))
250
40.7G
#define NEXTOPARG()  do { \
251
40.7G
        _Py_CODEUNIT word  = {.cache = FT_ATOMIC_LOAD_UINT16_RELAXED(*(uint16_t*)next_instr)}; \
252
40.7G
        opcode = word.op.code; \
253
40.7G
        oparg = word.op.arg; \
254
40.7G
    } while (0)
255
256
/* JUMPBY makes the generator identify the instruction as a jump. SKIP_OVER is
257
 * for advancing to the next instruction, taking into account cache entries
258
 * and skipped instructions.
259
 */
260
6.30G
#define JUMPBY(x)       (next_instr += (x))
261
#define SKIP_OVER(x)    (next_instr += (x))
262
263
#define STACK_LEVEL()     ((int)(stack_pointer - _PyFrame_Stackbase(frame)))
264
#define STACK_SIZE()      (_PyFrame_GetCode(frame)->co_stacksize)
265
266
#define WITHIN_STACK_BOUNDS() \
267
   (frame->owner == FRAME_OWNED_BY_INTERPRETER || (STACK_LEVEL() >= 0 && STACK_LEVEL() <= STACK_SIZE()))
268
269
#if defined(Py_DEBUG) && !defined(_Py_JIT)
270
// This allows temporary stack "overflows", provided it's all in the cache at any point of time.
271
#define WITHIN_STACK_BOUNDS_IGNORING_CACHE() \
272
   (frame->owner == FRAME_OWNED_BY_INTERPRETER || (STACK_LEVEL() >= 0 && (STACK_LEVEL()) <= STACK_SIZE()))
273
#else
274
#define WITHIN_STACK_BOUNDS_IGNORING_CACHE WITHIN_STACK_BOUNDS
275
#endif
276
277
/* Data access macros */
278
#define FRAME_CO_CONSTS (_PyFrame_GetCode(frame)->co_consts)
279
#define FRAME_CO_NAMES  (_PyFrame_GetCode(frame)->co_names)
280
281
/* Local variable macros */
282
283
1.29M
#define LOCALS_ARRAY    (frame->localsplus)
284
17.0G
#define GETLOCAL(i)     (frame->localsplus[i])
285
286
287
#ifdef Py_STATS
288
#define UPDATE_MISS_STATS(INSTNAME)                              \
289
    do {                                                         \
290
        STAT_INC(opcode, miss);                                  \
291
        STAT_INC((INSTNAME), miss);                              \
292
        /* The counter is always the first cache entry: */       \
293
        if (ADAPTIVE_COUNTER_TRIGGERS(next_instr->cache)) {       \
294
            STAT_INC((INSTNAME), deopt);                         \
295
        }                                                        \
296
    } while (0)
297
#else
298
257M
#define UPDATE_MISS_STATS(INSTNAME) ((void)0)
299
#endif
300
301
302
// Try to lock an object in the free threading build, if it's not already
303
// locked. Use with a DEOPT_IF() to deopt if the object is already locked.
304
// These are no-ops in the default GIL build. The general pattern is:
305
//
306
// DEOPT_IF(!LOCK_OBJECT(op));
307
// if (/* condition fails */) {
308
//     UNLOCK_OBJECT(op);
309
//     DEOPT_IF(true);
310
//  }
311
//  ...
312
//  UNLOCK_OBJECT(op);
313
//
314
// NOTE: The object must be unlocked on every exit code path and you should
315
// avoid any potentially escaping calls (like PyStackRef_CLOSE) while the
316
// object is locked.
317
#ifdef Py_GIL_DISABLED
318
#  define LOCK_OBJECT(op) PyMutex_LockFast(&(_PyObject_CAST(op))->ob_mutex)
319
#  define UNLOCK_OBJECT(op) PyMutex_Unlock(&(_PyObject_CAST(op))->ob_mutex)
320
#else
321
702M
#  define LOCK_OBJECT(op) (1)
322
702M
#  define UNLOCK_OBJECT(op) ((void)0)
323
#endif
324
325
1.78G
#define GLOBALS() frame->f_globals
326
684M
#define BUILTINS() frame->f_builtins
327
3.90M
#define LOCALS() frame->f_locals
328
#define CONSTS() _PyFrame_GetCode(frame)->co_consts
329
#define NAMES() _PyFrame_GetCode(frame)->co_names
330
331
#if defined(WITH_DTRACE) && !defined(Py_BUILD_CORE_MODULE)
332
static void dtrace_function_entry(_PyInterpreterFrame *);
333
static void dtrace_function_return(_PyInterpreterFrame *);
334
335
#define DTRACE_FUNCTION_ENTRY()  \
336
    if (PyDTrace_FUNCTION_ENTRY_ENABLED()) { \
337
        dtrace_function_entry(frame); \
338
    }
339
340
#define DTRACE_FUNCTION_RETURN() \
341
    if (PyDTrace_FUNCTION_RETURN_ENABLED()) { \
342
        dtrace_function_return(frame); \
343
    }
344
#else
345
1.26G
#define DTRACE_FUNCTION_ENTRY() ((void)0)
346
1.24G
#define DTRACE_FUNCTION_RETURN() ((void)0)
347
#endif
348
349
/* This takes a uint16_t instead of a _Py_BackoffCounter,
350
 * because it is used directly on the cache entry in generated code,
351
 * which is always an integral type. */
352
// Force re-specialization when tracing a side exit to get good side exits.
353
#define ADAPTIVE_COUNTER_TRIGGERS(COUNTER) \
354
1.62G
    backoff_counter_triggers(forge_backoff_counter((COUNTER)))
355
356
#define ADVANCE_ADAPTIVE_COUNTER(COUNTER) \
357
1.62G
    do { \
358
1.62G
        (COUNTER) = advance_backoff_counter((COUNTER)); \
359
1.62G
    } while (0);
360
361
#define PAUSE_ADAPTIVE_COUNTER(COUNTER) \
362
0
    do { \
363
0
        (COUNTER) = pause_backoff_counter((COUNTER)); \
364
0
    } while (0);
365
366
#ifdef ENABLE_SPECIALIZATION
367
/* Multiple threads may execute these concurrently if thread-local bytecode is
368
 * disabled and they all execute the main copy of the bytecode. Specialization
369
 * is disabled in that case so the value is unused, but the RMW cycle should be
370
 * free of data races.
371
 */
372
#define RECORD_BRANCH_TAKEN(bitset, flag) \
373
3.15G
    FT_ATOMIC_STORE_UINT16_RELAXED(       \
374
3.15G
        bitset, (FT_ATOMIC_LOAD_UINT16_RELAXED(bitset) << 1) | (flag))
375
#else
376
#define RECORD_BRANCH_TAKEN(bitset, flag)
377
#endif
378
379
#define UNBOUNDLOCAL_ERROR_MSG \
380
0
    "cannot access local variable '%s' where it is not associated with a value"
381
#define UNBOUNDFREE_ERROR_MSG \
382
0
    "cannot access free variable '%s' where it is not associated with a value" \
383
0
    " in enclosing scope"
384
6
#define NAME_ERROR_MSG "name '%.200s' is not defined"
385
386
// If a trace function sets a new f_lineno and
387
// *then* raises, we use the destination when searching
388
// for an exception handler, displaying the traceback, and so on
389
0
#define INSTRUMENTED_JUMP(src, dest, event) \
390
0
do { \
391
0
    if (tstate->tracing) {\
392
0
        next_instr = dest; \
393
0
    } else { \
394
0
        _PyFrame_SetStackPointer(frame, stack_pointer); \
395
0
        next_instr = _Py_call_instrumentation_jump(this_instr, tstate, event, frame, src, dest); \
396
0
        stack_pointer = _PyFrame_GetStackPointer(frame); \
397
0
        if (next_instr == NULL) { \
398
0
            next_instr = (dest)+1; \
399
0
            JUMP_TO_LABEL(error); \
400
0
        } \
401
0
    } \
402
0
} while (0);
403
404
405
264M
static inline int _Py_EnterRecursivePy(PyThreadState *tstate) {
406
264M
    return (tstate->py_recursion_remaining-- <= 0) &&
407
284
        _Py_CheckRecursiveCallPy(tstate);
408
264M
}
409
410
1.26G
static inline void _Py_LeaveRecursiveCallPy(PyThreadState *tstate)  {
411
1.26G
    tstate->py_recursion_remaining++;
412
1.26G
}
413
414
/* Implementation of "macros" that modify the instruction pointer,
415
 * stack pointer, or frame pointer.
416
 * These need to treated differently by tier 1 and 2.
417
 * The Tier 1 version is here; Tier 2 is inlined in ceval.c. */
418
419
2.22G
#define LOAD_IP(OFFSET) do { \
420
2.22G
        next_instr = frame->instr_ptr + (OFFSET); \
421
2.22G
    } while (0)
422
423
/* There's no STORE_IP(), it's inlined by the code generator. */
424
425
997M
#define LOAD_SP() \
426
997M
stack_pointer = _PyFrame_GetStackPointer(frame)
427
428
#define SAVE_SP() \
429
_PyFrame_SetStackPointer(frame, stack_pointer)
430
431
/* Tier-switching macros. */
432
433
#define TIER1_TO_TIER2(EXECUTOR)                        \
434
do {                                                   \
435
    OPT_STAT_INC(traces_executed);                     \
436
    next_instr = _Py_jit_entry((EXECUTOR), frame, stack_pointer, tstate); \
437
    frame = tstate->current_frame;                     \
438
    stack_pointer = _PyFrame_GetStackPointer(frame);   \
439
    int keep_tracing_bit = (uintptr_t)next_instr & 1;   \
440
    next_instr = (_Py_CODEUNIT *)(((uintptr_t)next_instr) & (~1)); \
441
    if (next_instr == NULL) {                          \
442
        /* gh-140104: The exception handler expects frame->instr_ptr
443
            to after this_instr, not this_instr! */ \
444
        next_instr = frame->instr_ptr + 1;                 \
445
        JUMP_TO_LABEL(error);                          \
446
    }                                                  \
447
    if (keep_tracing_bit) { \
448
        assert(uop_buffer_length(&((_PyThreadStateImpl *)tstate)->jit_tracer_state->code_buffer)); \
449
        ENTER_TRACING(); \
450
        DISPATCH_NON_TRACING(); \
451
    } \
452
    DISPATCH();                                        \
453
} while (0)
454
455
#define TIER2_TO_TIER2(EXECUTOR) \
456
do {                                                   \
457
    OPT_STAT_INC(traces_executed);                     \
458
    current_executor = (EXECUTOR);                     \
459
    goto tier2_start;                                  \
460
} while (0)
461
462
#define GOTO_TIER_ONE_SETUP \
463
    tstate->current_executor = NULL;                              \
464
    OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); \
465
    _PyFrame_SetStackPointer(frame, stack_pointer);
466
467
#define GOTO_TIER_ONE(TARGET) \
468
    do \
469
    { \
470
        GOTO_TIER_ONE_SETUP \
471
        return (_Py_CODEUNIT *)(TARGET); \
472
    } while (0)
473
474
#define GOTO_TIER_ONE_CONTINUE_TRACING(TARGET) \
475
    do \
476
    { \
477
        GOTO_TIER_ONE_SETUP \
478
        return (_Py_CODEUNIT *)(((uintptr_t)(TARGET))| 1); \
479
    } while (0)
480
481
#define CURRENT_OPARG()    (next_uop[-1].oparg)
482
#define CURRENT_OPERAND0_64() (next_uop[-1].operand0)
483
#define CURRENT_OPERAND1_64() (next_uop[-1].operand1)
484
#define CURRENT_OPERAND0_32() (next_uop[-1].operand0)
485
#define CURRENT_OPERAND1_32() (next_uop[-1].operand1)
486
#define CURRENT_OPERAND0_16() (next_uop[-1].operand0)
487
#define CURRENT_OPERAND1_16() (next_uop[-1].operand1)
488
#define CURRENT_TARGET()   (next_uop[-1].target)
489
490
#define JUMP_TO_JUMP_TARGET() goto jump_to_jump_target
491
#define JUMP_TO_ERROR() goto jump_to_error_target
492
493
/* Stackref macros */
494
495
/* How much scratch space to give stackref to PyObject* conversion. */
496
1.03G
#define MAX_STACKREF_SCRATCH 10
497
498
#define STACKREFS_TO_PYOBJECTS(ARGS, ARG_COUNT, NAME) \
499
    /* +1 because vectorcall might use -1 to write self */ \
500
1.03G
    PyObject *NAME##_temp[MAX_STACKREF_SCRATCH+1]; \
501
1.03G
    PyObject **NAME = _PyObjectArray_FromStackRefArray(ARGS, ARG_COUNT, NAME##_temp);
502
503
#define STACKREFS_TO_PYOBJECTS_CLEANUP(NAME) \
504
    /* +1 because we +1 previously */ \
505
1.03G
    _PyObjectArray_Free(NAME - 1, NAME##_temp);
506
507
1.03G
#define CONVERSION_FAILED(NAME) ((NAME) == NULL)
508
509
#if defined(Py_DEBUG) && !defined(_Py_JIT)
510
#define SET_CURRENT_CACHED_VALUES(N) current_cached_values = (N)
511
#define CHECK_CURRENT_CACHED_VALUES(N) assert(current_cached_values == (N))
512
#else
513
#define SET_CURRENT_CACHED_VALUES(N) ((void)0)
514
#define CHECK_CURRENT_CACHED_VALUES(N) ((void)0)
515
#endif
516
517
1.05G
#define IS_PEP523_HOOKED(tstate) (tstate->interp->eval_frame != NULL)
518
519
static inline int
520
2.37G
check_periodics(PyThreadState *tstate) {
521
2.37G
    _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY();
522
2.37G
    QSBR_QUIESCENT_STATE(tstate);
523
2.37G
    if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) {
524
111k
        return _Py_HandlePending(tstate);
525
111k
    }
526
2.37G
    return 0;
527
2.37G
}
528
529
// Mark the generator as executing. Returns true if the state was changed,
530
// false if it was already executing or finished.
531
static inline bool
532
gen_try_set_executing(PyGenObject *gen)
533
28.6M
{
534
#ifdef Py_GIL_DISABLED
535
    if (!_PyObject_IsUniquelyReferenced((PyObject *)gen)) {
536
        int8_t frame_state = _Py_atomic_load_int8_relaxed(&gen->gi_frame_state);
537
        while (frame_state < FRAME_SUSPENDED_YIELD_FROM_LOCKED) {
538
            if (_Py_atomic_compare_exchange_int8(&gen->gi_frame_state,
539
                                                 &frame_state,
540
                                                 FRAME_EXECUTING)) {
541
                return true;
542
            }
543
        }
544
        // NB: We return false for FRAME_SUSPENDED_YIELD_FROM_LOCKED as well.
545
        // That case is rare enough that we can just handle it in the deopt.
546
        return false;
547
    }
548
#endif
549
    // Use faster non-atomic modifications in the GIL-enabled build and when
550
    // the object is uniquely referenced in the free-threaded build.
551
28.6M
    if (gen->gi_frame_state < FRAME_EXECUTING) {
552
28.6M
        assert(gen->gi_frame_state != FRAME_SUSPENDED_YIELD_FROM_LOCKED);
553
28.6M
        gen->gi_frame_state = FRAME_EXECUTING;
554
28.6M
        return true;
555
28.6M
    }
556
0
    return false;
557
28.6M
}
558
559
// Macro for inplace float binary ops (tier 2 only).
560
// Mutates the uniquely-referenced TARGET operand in place.
561
// TARGET must be either left or right.
562
#define FLOAT_INPLACE_OP(left, right, TARGET, OP)                        \
563
    do {                                                                 \
564
        PyObject *left_o = PyStackRef_AsPyObjectBorrow(left);            \
565
        PyObject *right_o = PyStackRef_AsPyObjectBorrow(right);          \
566
        assert(PyFloat_CheckExact(left_o));                              \
567
        assert(PyFloat_CheckExact(right_o));                             \
568
        assert(_PyObject_IsUniquelyReferenced(                           \
569
            PyStackRef_AsPyObjectBorrow(TARGET)));                       \
570
        STAT_INC(BINARY_OP, hit);                                        \
571
        double _dres =                                                   \
572
            ((PyFloatObject *)left_o)->ob_fval                           \
573
            OP ((PyFloatObject *)right_o)->ob_fval;                      \
574
        ((PyFloatObject *)PyStackRef_AsPyObjectBorrow(TARGET))           \
575
            ->ob_fval = _dres;                                           \
576
    } while (0)
577
578
// Inplace float true division. Sets _divop_err to 1 on zero division.
579
// Caller must check _divop_err and call ERROR_NO_POP() if set.
580
#define FLOAT_INPLACE_DIVOP(left, right, TARGET)                         \
581
    int _divop_err = 0;                                                  \
582
    do {                                                                 \
583
        PyObject *left_o = PyStackRef_AsPyObjectBorrow(left);            \
584
        PyObject *right_o = PyStackRef_AsPyObjectBorrow(right);          \
585
        assert(PyFloat_CheckExact(left_o));                              \
586
        assert(PyFloat_CheckExact(right_o));                             \
587
        assert(_PyObject_IsUniquelyReferenced(                           \
588
            PyStackRef_AsPyObjectBorrow(TARGET)));                       \
589
        STAT_INC(BINARY_OP, hit);                                        \
590
        double _divisor = ((PyFloatObject *)right_o)->ob_fval;           \
591
        if (_divisor == 0.0) {                                           \
592
            PyErr_SetString(PyExc_ZeroDivisionError,                     \
593
                            "float division by zero");                   \
594
            _divop_err = 1;                                              \
595
            break;                                                       \
596
        }                                                                \
597
        double _dres = ((PyFloatObject *)left_o)->ob_fval / _divisor;    \
598
        ((PyFloatObject *)PyStackRef_AsPyObjectBorrow(TARGET))           \
599
            ->ob_fval = _dres;                                           \
600
    } while (0)
601
602
// Inplace compact int operation. TARGET is expected to be uniquely
603
// referenced at the optimizer level, but at runtime it may be a
604
// cached small int singleton. We check _Py_IsImmortal on TARGET
605
// to decide whether inplace mutation is safe.
606
//
607
// After the macro, _int_inplace_res holds the result (may be NULL
608
// on allocation failure). On success, TARGET was mutated in place
609
// and _int_inplace_res is a DUP'd reference to it. On fallback
610
// (small int target, small int result, or overflow), _int_inplace_res
611
// is from FUNC (_PyCompactLong_Add etc.).
612
// FUNC is the fallback function (_PyCompactLong_Add etc.)
613
#define INT_INPLACE_OP(left, right, TARGET, OP, FUNC)                    \
614
    _PyStackRef _int_inplace_res = PyStackRef_NULL;                      \
615
    do {                                                                 \
616
        PyObject *target_o = PyStackRef_AsPyObjectBorrow(TARGET);        \
617
        if (_Py_IsImmortal(target_o)) {                                  \
618
            break;                                                       \
619
        }                                                                \
620
        assert(_PyObject_IsUniquelyReferenced(target_o));                \
621
        Py_ssize_t left_val = _PyLong_CompactValue(                      \
622
            (PyLongObject *)PyStackRef_AsPyObjectBorrow(left));          \
623
        Py_ssize_t right_val = _PyLong_CompactValue(                     \
624
            (PyLongObject *)PyStackRef_AsPyObjectBorrow(right));         \
625
        Py_ssize_t result = left_val OP right_val;                       \
626
        if (!_PY_IS_SMALL_INT(result)                                    \
627
            && ((twodigits)((stwodigits)result) + PyLong_MASK            \
628
                < (twodigits)PyLong_MASK + PyLong_BASE))                 \
629
        {                                                                \
630
            _PyLong_SetSignAndDigitCount(                                \
631
                (PyLongObject *)target_o, result < 0 ? -1 : 1, 1);       \
632
            ((PyLongObject *)target_o)->long_value.ob_digit[0] =         \
633
                (digit)(result < 0 ? -result : result);                  \
634
            _int_inplace_res = PyStackRef_DUP(TARGET);                   \
635
            break;                                                       \
636
        }                                                                \
637
    } while (0);                                                         \
638
    if (PyStackRef_IsNull(_int_inplace_res)) {                           \
639
        _int_inplace_res = FUNC(                                         \
640
            (PyLongObject *)PyStackRef_AsPyObjectBorrow(left),           \
641
            (PyLongObject *)PyStackRef_AsPyObjectBorrow(right));         \
642
    }
643
644
#define CALL_TP_ITERITEM_NO_ESCAPE(ITER, INDEX) \
645
    Py_TYPE(ITER)->_tp_iteritem((ITER), (INDEX))