Coverage Report

Created: 2026-02-26 06:53

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