Coverage Report

Created: 2025-07-04 06:49

/src/cpython/Python/instruction_sequence.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * This file implements a data structure representing a sequence of
3
 * instructions, which is used by different parts of the compilation
4
 * pipeline.
5
 */
6
7
8
#include "Python.h"
9
10
#include "pycore_c_array.h" // _Py_CArray_EnsureCapacity
11
#include "pycore_compile.h" // _PyInstruction
12
#include "pycore_opcode_utils.h"
13
#include "pycore_opcode_metadata.h" // OPCODE_HAS_ARG, etc
14
15
typedef _PyInstruction instruction;
16
typedef _PyInstructionSequence instr_sequence;
17
typedef _Py_SourceLocation location;
18
19
601k
#define INITIAL_INSTR_SEQUENCE_SIZE 100
20
73.8k
#define INITIAL_INSTR_SEQUENCE_LABELS_MAP_SIZE 10
21
22
#include "clinic/instruction_sequence.c.h"
23
24
#include <stdbool.h>
25
26
#undef SUCCESS
27
#undef ERROR
28
692k
#define SUCCESS 0
29
0
#define ERROR -1
30
31
#define RETURN_IF_ERROR(X)  \
32
1.27M
    if ((X) == -1) {        \
33
0
        return ERROR;       \
34
0
    }
35
36
static int
37
601k
instr_sequence_next_inst(instr_sequence *seq) {
38
601k
    assert(seq->s_instrs != NULL || seq->s_used == 0);
39
40
41
601k
    _Py_c_array_t array = {
42
601k
        .array = (void*)seq->s_instrs,
43
601k
        .allocated_entries = seq->s_allocated,
44
601k
        .item_size = sizeof(instruction),
45
601k
        .initial_num_entries = INITIAL_INSTR_SEQUENCE_SIZE,
46
601k
    };
47
48
601k
    RETURN_IF_ERROR(_Py_CArray_EnsureCapacity(&array, seq->s_used + 1));
49
601k
    seq->s_instrs = array.array;
50
601k
    seq->s_allocated = array.allocated_entries;
51
52
601k
    assert(seq->s_allocated >= 0);
53
601k
    assert(seq->s_used < seq->s_allocated);
54
601k
    return seq->s_used++;
55
601k
}
56
57
_PyJumpTargetLabel
58
_PyInstructionSequence_NewLabel(instr_sequence *seq)
59
35.2k
{
60
35.2k
    _PyJumpTargetLabel lbl = {++seq->s_next_free_label};
61
35.2k
    return lbl;
62
35.2k
}
63
64
int
65
_PyInstructionSequence_UseLabel(instr_sequence *seq, int lbl)
66
73.8k
{
67
73.8k
    int old_size = seq->s_labelmap_size;
68
73.8k
    _Py_c_array_t array = {
69
73.8k
        .array = (void*)seq->s_labelmap,
70
73.8k
        .allocated_entries = seq->s_labelmap_size,
71
73.8k
        .item_size = sizeof(int),
72
73.8k
        .initial_num_entries = INITIAL_INSTR_SEQUENCE_LABELS_MAP_SIZE,
73
73.8k
    };
74
75
73.8k
    RETURN_IF_ERROR(_Py_CArray_EnsureCapacity(&array, lbl));
76
73.8k
    seq->s_labelmap = array.array;
77
73.8k
    seq->s_labelmap_size = array.allocated_entries;
78
79
227k
    for(int i = old_size; i < seq->s_labelmap_size; i++) {
80
153k
        seq->s_labelmap[i] = -111;  /* something weird, for debugging */
81
153k
    }
82
73.8k
    seq->s_labelmap[lbl] = seq->s_used; /* label refers to the next instruction */
83
73.8k
    return SUCCESS;
84
73.8k
}
85
86
int
87
_PyInstructionSequence_ApplyLabelMap(instr_sequence *instrs)
88
17.3k
{
89
17.3k
    if (instrs->s_labelmap == NULL) {
90
        /* Already applied - nothing to do */
91
6.94k
        return SUCCESS;
92
6.94k
    }
93
    /* Replace labels by offsets in the code */
94
577k
    for (int i=0; i < instrs->s_used; i++) {
95
567k
        instruction *instr = &instrs->s_instrs[i];
96
567k
        if (HAS_TARGET(instr->i_opcode)) {
97
34.5k
            assert(instr->i_oparg < instrs->s_labelmap_size);
98
34.5k
            instr->i_oparg = instrs->s_labelmap[instr->i_oparg];
99
34.5k
        }
100
567k
        _PyExceptHandlerInfo *hi = &instr->i_except_handler_info;
101
567k
        if (hi->h_label >= 0) {
102
319k
            assert(hi->h_label < instrs->s_labelmap_size);
103
319k
            hi->h_label = instrs->s_labelmap[hi->h_label];
104
319k
        }
105
567k
    }
106
    /* Clear label map so it's never used again */
107
10.4k
    PyMem_Free(instrs->s_labelmap);
108
10.4k
    instrs->s_labelmap = NULL;
109
10.4k
    instrs->s_labelmap_size = 0;
110
10.4k
    return SUCCESS;
111
17.3k
}
112
113
#define MAX_OPCODE 511
114
115
int
116
_PyInstructionSequence_Addop(instr_sequence *seq, int opcode, int oparg,
117
                             location loc)
118
601k
{
119
601k
    assert(0 <= opcode && opcode <= MAX_OPCODE);
120
601k
    assert(IS_WITHIN_OPCODE_RANGE(opcode));
121
601k
    assert(OPCODE_HAS_ARG(opcode) || HAS_TARGET(opcode) || oparg == 0);
122
601k
    assert(0 <= oparg && oparg < (1 << 30));
123
124
601k
    int idx = instr_sequence_next_inst(seq);
125
601k
    RETURN_IF_ERROR(idx);
126
601k
    instruction *ci = &seq->s_instrs[idx];
127
601k
    ci->i_opcode = opcode;
128
601k
    ci->i_oparg = oparg;
129
601k
    ci->i_loc = loc;
130
601k
    return SUCCESS;
131
601k
}
132
133
int
134
_PyInstructionSequence_InsertInstruction(instr_sequence *seq, int pos,
135
                                         int opcode, int oparg, location loc)
136
307
{
137
307
    assert(pos >= 0 && pos <= seq->s_used);
138
307
    int last_idx = instr_sequence_next_inst(seq);
139
307
    RETURN_IF_ERROR(last_idx);
140
12.8k
    for (int i=last_idx-1; i >= pos; i--) {
141
12.5k
        seq->s_instrs[i+1] = seq->s_instrs[i];
142
12.5k
    }
143
307
    instruction *ci = &seq->s_instrs[pos];
144
307
    ci->i_opcode = opcode;
145
307
    ci->i_oparg = oparg;
146
307
    ci->i_loc = loc;
147
148
    /* fix the labels map */
149
4.84k
    for(int lbl=0; lbl < seq->s_labelmap_size; lbl++) {
150
4.54k
        if (seq->s_labelmap[lbl] >= pos) {
151
2.34k
            seq->s_labelmap[lbl]++;
152
2.34k
        }
153
4.54k
    }
154
307
    return SUCCESS;
155
307
}
156
157
int
158
_PyInstructionSequence_SetAnnotationsCode(instr_sequence *seq,
159
                                          instr_sequence *annotations)
160
0
{
161
0
    assert(seq->s_annotations_code == NULL);
162
0
    seq->s_annotations_code = annotations;
163
0
    return SUCCESS;
164
0
}
165
166
int
167
_PyInstructionSequence_AddNested(instr_sequence *seq, instr_sequence *nested)
168
0
{
169
0
    if (seq->s_nested == NULL) {
170
0
        seq->s_nested = PyList_New(0);
171
0
        if (seq->s_nested == NULL) {
172
0
            return ERROR;
173
0
        }
174
0
    }
175
0
    if (PyList_Append(seq->s_nested, (PyObject*)nested) < 0) {
176
0
        return ERROR;
177
0
    }
178
0
    return SUCCESS;
179
0
}
180
181
void
182
11.5k
PyInstructionSequence_Fini(instr_sequence *seq) {
183
11.5k
    Py_XDECREF(seq->s_nested);
184
185
11.5k
    PyMem_Free(seq->s_labelmap);
186
11.5k
    seq->s_labelmap = NULL;
187
188
11.5k
    PyMem_Free(seq->s_instrs);
189
11.5k
    seq->s_instrs = NULL;
190
191
11.5k
    if (seq->s_annotations_code != NULL) {
192
0
        PyInstructionSequence_Fini(seq->s_annotations_code);
193
0
        Py_CLEAR(seq->s_annotations_code);
194
0
    }
195
196
11.5k
}
197
198
/*[clinic input]
199
class InstructionSequenceType "_PyInstructionSequence *" "&_PyInstructionSequence_Type"
200
[clinic start generated code]*/
201
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=589963e07480390f]*/
202
203
static _PyInstructionSequence*
204
inst_seq_create(void)
205
5.78k
{
206
5.78k
    _PyInstructionSequence *seq;
207
5.78k
    seq = PyObject_GC_New(_PyInstructionSequence, &_PyInstructionSequence_Type);
208
5.78k
    if (seq == NULL) {
209
0
        return NULL;
210
0
    }
211
5.78k
    seq->s_instrs = NULL;
212
5.78k
    seq->s_allocated = 0;
213
5.78k
    seq->s_used = 0;
214
5.78k
    seq->s_next_free_label = 0;
215
5.78k
    seq->s_labelmap = NULL;
216
5.78k
    seq->s_labelmap_size = 0;
217
5.78k
    seq->s_nested = NULL;
218
5.78k
    seq->s_annotations_code = NULL;
219
220
5.78k
    PyObject_GC_Track(seq);
221
5.78k
    return seq;
222
5.78k
}
223
224
PyObject*
225
_PyInstructionSequence_New(void)
226
5.78k
{
227
5.78k
    _PyInstructionSequence *seq = inst_seq_create();
228
5.78k
    if (seq == NULL) {
229
0
        return NULL;
230
0
    }
231
5.78k
    return (PyObject*)seq;
232
5.78k
}
233
234
/*[clinic input]
235
@classmethod
236
InstructionSequenceType.__new__ as inst_seq_new
237
238
Create a new InstructionSequence object.
239
[clinic start generated code]*/
240
241
static PyObject *
242
inst_seq_new_impl(PyTypeObject *type)
243
/*[clinic end generated code: output=98881de92c8876f6 input=b393150146849c74]*/
244
0
{
245
0
    return (PyObject*)inst_seq_create();
246
0
}
247
248
/*[clinic input]
249
InstructionSequenceType.use_label
250
251
  label: int
252
253
Place label at current location.
254
[clinic start generated code]*/
255
256
static PyObject *
257
InstructionSequenceType_use_label_impl(_PyInstructionSequence *self,
258
                                       int label)
259
/*[clinic end generated code: output=4c06bbacb2854755 input=da55f49bb91841f3]*/
260
261
0
{
262
0
    if (_PyInstructionSequence_UseLabel(self, label) < 0) {
263
0
        return NULL;
264
0
    }
265
0
    Py_RETURN_NONE;
266
0
}
267
268
/*[clinic input]
269
InstructionSequenceType.addop
270
271
  opcode: int
272
  oparg: int
273
  lineno: int
274
  col_offset: int
275
  end_lineno: int
276
  end_col_offset: int
277
278
Append an instruction.
279
[clinic start generated code]*/
280
281
static PyObject *
282
InstructionSequenceType_addop_impl(_PyInstructionSequence *self, int opcode,
283
                                   int oparg, int lineno, int col_offset,
284
                                   int end_lineno, int end_col_offset)
285
/*[clinic end generated code: output=af0cc22c048dfbf3 input=012762ac88198713]*/
286
0
{
287
0
    _Py_SourceLocation loc = {lineno, col_offset, end_lineno, end_col_offset};
288
0
    if (_PyInstructionSequence_Addop(self, opcode, oparg, loc) < 0) {
289
0
        return NULL;
290
0
    }
291
0
    Py_RETURN_NONE;
292
0
}
293
294
/*[clinic input]
295
InstructionSequenceType.new_label -> int
296
297
Return a new label.
298
[clinic start generated code]*/
299
300
static int
301
InstructionSequenceType_new_label_impl(_PyInstructionSequence *self)
302
/*[clinic end generated code: output=dcb0589e4f5bf4bd input=c66040b9897bc327]*/
303
0
{
304
0
    _PyJumpTargetLabel lbl = _PyInstructionSequence_NewLabel(self);
305
0
    return lbl.id;
306
0
}
307
308
/*[clinic input]
309
InstructionSequenceType.add_nested
310
311
  nested: object
312
313
Add a nested sequence.
314
[clinic start generated code]*/
315
316
static PyObject *
317
InstructionSequenceType_add_nested_impl(_PyInstructionSequence *self,
318
                                        PyObject *nested)
319
/*[clinic end generated code: output=14540fad459f7971 input=f2c482568b3b3c0f]*/
320
0
{
321
0
    if (!_PyInstructionSequence_Check(nested)) {
322
0
        PyErr_Format(PyExc_TypeError,
323
0
                     "expected an instruction sequence, not %T",
324
0
                     Py_TYPE(nested));
325
0
        return NULL;
326
0
    }
327
0
    if (_PyInstructionSequence_AddNested(self, (_PyInstructionSequence*)nested) < 0) {
328
0
        return NULL;
329
0
    }
330
0
    Py_RETURN_NONE;
331
0
}
332
333
/*[clinic input]
334
InstructionSequenceType.get_nested
335
336
Add a nested sequence.
337
[clinic start generated code]*/
338
339
static PyObject *
340
InstructionSequenceType_get_nested_impl(_PyInstructionSequence *self)
341
/*[clinic end generated code: output=f415112c292630cb input=e429e474c57b95b4]*/
342
0
{
343
0
    if (self->s_nested == NULL) {
344
0
        return PyList_New(0);
345
0
    }
346
0
    return Py_NewRef(self->s_nested);
347
0
}
348
349
/*[clinic input]
350
InstructionSequenceType.get_instructions
351
352
Return the instructions as a list of tuples or labels.
353
[clinic start generated code]*/
354
355
static PyObject *
356
InstructionSequenceType_get_instructions_impl(_PyInstructionSequence *self)
357
/*[clinic end generated code: output=23f4f3f894c301b3 input=fbadb5dadb611291]*/
358
0
{
359
0
    if (_PyInstructionSequence_ApplyLabelMap(self) < 0) {
360
0
        return NULL;
361
0
    }
362
0
    PyObject *instructions = PyList_New(0);
363
0
    if (instructions == NULL) {
364
0
        return NULL;
365
0
    }
366
0
    for (int i = 0; i < self->s_used; i++) {
367
0
        instruction *instr = &self->s_instrs[i];
368
0
        location loc = instr->i_loc;
369
0
        PyObject *inst_tuple;
370
371
0
        if (OPCODE_HAS_ARG(instr->i_opcode)) {
372
0
            inst_tuple = Py_BuildValue(
373
0
                "(iiiiii)", instr->i_opcode, instr->i_oparg,
374
0
                loc.lineno, loc.end_lineno,
375
0
                loc.col_offset, loc.end_col_offset);
376
0
        }
377
0
        else {
378
0
            inst_tuple = Py_BuildValue(
379
0
                "(iOiiii)", instr->i_opcode, Py_None,
380
0
                loc.lineno, loc.end_lineno,
381
0
                loc.col_offset, loc.end_col_offset);
382
0
        }
383
0
        if (inst_tuple == NULL) {
384
0
            goto error;
385
0
        }
386
387
0
        int res = PyList_Append(instructions, inst_tuple);
388
0
        Py_DECREF(inst_tuple);
389
0
        if (res != 0) {
390
0
            goto error;
391
0
        }
392
0
    }
393
0
    return instructions;
394
0
error:
395
0
    Py_XDECREF(instructions);
396
0
    return NULL;
397
0
}
398
399
static PyMethodDef inst_seq_methods[] = {
400
   INSTRUCTIONSEQUENCETYPE_ADDOP_METHODDEF
401
   INSTRUCTIONSEQUENCETYPE_NEW_LABEL_METHODDEF
402
   INSTRUCTIONSEQUENCETYPE_USE_LABEL_METHODDEF
403
   INSTRUCTIONSEQUENCETYPE_ADD_NESTED_METHODDEF
404
   INSTRUCTIONSEQUENCETYPE_GET_NESTED_METHODDEF
405
   INSTRUCTIONSEQUENCETYPE_GET_INSTRUCTIONS_METHODDEF
406
   {NULL, NULL, 0, NULL},
407
};
408
409
static PyMemberDef inst_seq_memberlist[] = {
410
    {NULL}      /* Sentinel */
411
};
412
413
static PyGetSetDef inst_seq_getsetters[] = {
414
    {NULL}      /* Sentinel */
415
};
416
417
static void
418
inst_seq_dealloc(PyObject *op)
419
5.78k
{
420
5.78k
    _PyInstructionSequence *seq = (_PyInstructionSequence *)op;
421
5.78k
    PyObject_GC_UnTrack(seq);
422
5.78k
    PyInstructionSequence_Fini(seq);
423
5.78k
    PyObject_GC_Del(seq);
424
5.78k
}
425
426
static int
427
inst_seq_traverse(PyObject *op, visitproc visit, void *arg)
428
0
{
429
0
    _PyInstructionSequence *seq = (_PyInstructionSequence *)op;
430
0
    Py_VISIT(seq->s_nested);
431
0
    Py_VISIT((PyObject *)seq->s_annotations_code);
432
0
    return 0;
433
0
}
434
435
static int
436
inst_seq_clear(PyObject *op)
437
0
{
438
0
    _PyInstructionSequence *seq = (_PyInstructionSequence *)op;
439
0
    Py_CLEAR(seq->s_nested);
440
0
    Py_CLEAR(seq->s_annotations_code);
441
0
    return 0;
442
0
}
443
444
PyTypeObject _PyInstructionSequence_Type = {
445
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
446
    "InstructionSequence",
447
    sizeof(_PyInstructionSequence),
448
    0,
449
    inst_seq_dealloc,   /*tp_dealloc*/
450
    0,                  /*tp_vectorcall_offset*/
451
    0,                  /*tp_getattr*/
452
    0,                  /*tp_setattr*/
453
    0,                  /*tp_as_async*/
454
    0,                  /*tp_repr*/
455
    0,                  /*tp_as_number*/
456
    0,                  /*tp_as_sequence*/
457
    0,                  /*tp_as_mapping*/
458
    0,                  /* tp_hash */
459
    0,                  /* tp_call */
460
    0,                  /* tp_str */
461
    PyObject_GenericGetAttr,  /* tp_getattro */
462
    0,                  /* tp_setattro */
463
    0,                  /* tp_as_buffer */
464
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
465
    inst_seq_new__doc__,                    /* tp_doc */
466
    inst_seq_traverse,                      /* tp_traverse */
467
    inst_seq_clear,                         /* tp_clear */
468
    0,                                      /* tp_richcompare */
469
    0,                                      /* tp_weaklistoffset */
470
    0,                                      /* tp_iter */
471
    0,                                      /* tp_iternext */
472
    inst_seq_methods,                       /* tp_methods */
473
    inst_seq_memberlist,                    /* tp_members */
474
    inst_seq_getsetters,                    /* tp_getset */
475
    0,                                      /* tp_base */
476
    0,                                      /* tp_dict */
477
    0,                                      /* tp_descr_get */
478
    0,                                      /* tp_descr_set */
479
    0,                                      /* tp_dictoffset */
480
    0,                                      /* tp_init */
481
    0,                                      /* tp_alloc */
482
    inst_seq_new,                           /* tp_new */
483
};