mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 07:31:38 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			467 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			467 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * This file implements a data structure representing a sequence of
 | 
						|
 * instructions, which is used by different parts of the compilation
 | 
						|
 * pipeline.
 | 
						|
 */
 | 
						|
 | 
						|
 | 
						|
#include "Python.h"
 | 
						|
 | 
						|
#include "pycore_c_array.h" // _Py_CArray_EnsureCapacity
 | 
						|
#include "pycore_compile.h" // _PyInstruction
 | 
						|
#include "pycore_opcode_utils.h"
 | 
						|
#include "pycore_opcode_metadata.h" // OPCODE_HAS_ARG, etc
 | 
						|
 | 
						|
typedef _PyInstruction instruction;
 | 
						|
typedef _PyInstructionSequence instr_sequence;
 | 
						|
typedef _Py_SourceLocation location;
 | 
						|
 | 
						|
#define INITIAL_INSTR_SEQUENCE_SIZE 100
 | 
						|
#define INITIAL_INSTR_SEQUENCE_LABELS_MAP_SIZE 10
 | 
						|
 | 
						|
#include "clinic/instruction_sequence.c.h"
 | 
						|
 | 
						|
#include <stdbool.h>
 | 
						|
 | 
						|
#undef SUCCESS
 | 
						|
#undef ERROR
 | 
						|
#define SUCCESS 0
 | 
						|
#define ERROR -1
 | 
						|
 | 
						|
#define RETURN_IF_ERROR(X)  \
 | 
						|
    if ((X) == -1) {        \
 | 
						|
        return ERROR;       \
 | 
						|
    }
 | 
						|
 | 
						|
static int
 | 
						|
instr_sequence_next_inst(instr_sequence *seq) {
 | 
						|
    assert(seq->s_instrs != NULL || seq->s_used == 0);
 | 
						|
 | 
						|
 | 
						|
    _Py_c_array_t array = {
 | 
						|
        .array = (void*)seq->s_instrs,
 | 
						|
        .allocated_entries = seq->s_allocated,
 | 
						|
        .item_size = sizeof(instruction),
 | 
						|
        .initial_num_entries = INITIAL_INSTR_SEQUENCE_SIZE,
 | 
						|
    };
 | 
						|
 | 
						|
    RETURN_IF_ERROR(_Py_CArray_EnsureCapacity(&array, seq->s_used + 1));
 | 
						|
    seq->s_instrs = array.array;
 | 
						|
    seq->s_allocated = array.allocated_entries;
 | 
						|
 | 
						|
    assert(seq->s_allocated >= 0);
 | 
						|
    assert(seq->s_used < seq->s_allocated);
 | 
						|
    return seq->s_used++;
 | 
						|
}
 | 
						|
 | 
						|
_PyJumpTargetLabel
 | 
						|
_PyInstructionSequence_NewLabel(instr_sequence *seq)
 | 
						|
{
 | 
						|
    _PyJumpTargetLabel lbl = {++seq->s_next_free_label};
 | 
						|
    return lbl;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
_PyInstructionSequence_UseLabel(instr_sequence *seq, int lbl)
 | 
						|
{
 | 
						|
    int old_size = seq->s_labelmap_size;
 | 
						|
    _Py_c_array_t array = {
 | 
						|
        .array = (void*)seq->s_labelmap,
 | 
						|
        .allocated_entries = seq->s_labelmap_size,
 | 
						|
        .item_size = sizeof(int),
 | 
						|
        .initial_num_entries = INITIAL_INSTR_SEQUENCE_LABELS_MAP_SIZE,
 | 
						|
    };
 | 
						|
 | 
						|
    RETURN_IF_ERROR(_Py_CArray_EnsureCapacity(&array, lbl));
 | 
						|
    seq->s_labelmap = array.array;
 | 
						|
    seq->s_labelmap_size = array.allocated_entries;
 | 
						|
 | 
						|
    for(int i = old_size; i < seq->s_labelmap_size; i++) {
 | 
						|
        seq->s_labelmap[i] = -111;  /* something weird, for debugging */
 | 
						|
    }
 | 
						|
    seq->s_labelmap[lbl] = seq->s_used; /* label refers to the next instruction */
 | 
						|
    return SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
_PyInstructionSequence_ApplyLabelMap(instr_sequence *instrs)
 | 
						|
{
 | 
						|
    if (instrs->s_labelmap == NULL) {
 | 
						|
        /* Already applied - nothing to do */
 | 
						|
        return SUCCESS;
 | 
						|
    }
 | 
						|
    /* Replace labels by offsets in the code */
 | 
						|
    for (int i=0; i < instrs->s_used; i++) {
 | 
						|
        instruction *instr = &instrs->s_instrs[i];
 | 
						|
        if (HAS_TARGET(instr->i_opcode)) {
 | 
						|
            assert(instr->i_oparg < instrs->s_labelmap_size);
 | 
						|
            instr->i_oparg = instrs->s_labelmap[instr->i_oparg];
 | 
						|
        }
 | 
						|
        _PyExceptHandlerInfo *hi = &instr->i_except_handler_info;
 | 
						|
        if (hi->h_label >= 0) {
 | 
						|
            assert(hi->h_label < instrs->s_labelmap_size);
 | 
						|
            hi->h_label = instrs->s_labelmap[hi->h_label];
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /* Clear label map so it's never used again */
 | 
						|
    PyMem_Free(instrs->s_labelmap);
 | 
						|
    instrs->s_labelmap = NULL;
 | 
						|
    instrs->s_labelmap_size = 0;
 | 
						|
    return SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
#define MAX_OPCODE 511
 | 
						|
 | 
						|
int
 | 
						|
_PyInstructionSequence_Addop(instr_sequence *seq, int opcode, int oparg,
 | 
						|
                             location loc)
 | 
						|
{
 | 
						|
    assert(0 <= opcode && opcode <= MAX_OPCODE);
 | 
						|
    assert(IS_WITHIN_OPCODE_RANGE(opcode));
 | 
						|
    assert(OPCODE_HAS_ARG(opcode) || HAS_TARGET(opcode) || oparg == 0);
 | 
						|
    assert(0 <= oparg && oparg < (1 << 30));
 | 
						|
 | 
						|
    int idx = instr_sequence_next_inst(seq);
 | 
						|
    RETURN_IF_ERROR(idx);
 | 
						|
    instruction *ci = &seq->s_instrs[idx];
 | 
						|
    ci->i_opcode = opcode;
 | 
						|
    ci->i_oparg = oparg;
 | 
						|
    ci->i_loc = loc;
 | 
						|
    return SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
_PyInstructionSequence_InsertInstruction(instr_sequence *seq, int pos,
 | 
						|
                                         int opcode, int oparg, location loc)
 | 
						|
{
 | 
						|
    assert(pos >= 0 && pos <= seq->s_used);
 | 
						|
    int last_idx = instr_sequence_next_inst(seq);
 | 
						|
    RETURN_IF_ERROR(last_idx);
 | 
						|
    for (int i=last_idx-1; i >= pos; i--) {
 | 
						|
        seq->s_instrs[i+1] = seq->s_instrs[i];
 | 
						|
    }
 | 
						|
    instruction *ci = &seq->s_instrs[pos];
 | 
						|
    ci->i_opcode = opcode;
 | 
						|
    ci->i_oparg = oparg;
 | 
						|
    ci->i_loc = loc;
 | 
						|
 | 
						|
    /* fix the labels map */
 | 
						|
    for(int lbl=0; lbl < seq->s_labelmap_size; lbl++) {
 | 
						|
        if (seq->s_labelmap[lbl] >= pos) {
 | 
						|
            seq->s_labelmap[lbl]++;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
_PyInstructionSequence_AddNested(instr_sequence *seq, instr_sequence *nested)
 | 
						|
{
 | 
						|
    if (seq->s_nested == NULL) {
 | 
						|
        seq->s_nested = PyList_New(0);
 | 
						|
        if (seq->s_nested == NULL) {
 | 
						|
            return ERROR;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    if (PyList_Append(seq->s_nested, (PyObject*)nested) < 0) {
 | 
						|
        return ERROR;
 | 
						|
    }
 | 
						|
    return SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
PyInstructionSequence_Fini(instr_sequence *seq) {
 | 
						|
    Py_XDECREF(seq->s_nested);
 | 
						|
 | 
						|
    PyMem_Free(seq->s_labelmap);
 | 
						|
    seq->s_labelmap = NULL;
 | 
						|
 | 
						|
    PyMem_Free(seq->s_instrs);
 | 
						|
    seq->s_instrs = NULL;
 | 
						|
}
 | 
						|
 | 
						|
/*[clinic input]
 | 
						|
class InstructionSequenceType "_PyInstructionSequence *" "&_PyInstructionSequence_Type"
 | 
						|
[clinic start generated code]*/
 | 
						|
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=589963e07480390f]*/
 | 
						|
 | 
						|
static _PyInstructionSequence*
 | 
						|
inst_seq_create(void)
 | 
						|
{
 | 
						|
    _PyInstructionSequence *seq;
 | 
						|
    seq = PyObject_GC_New(_PyInstructionSequence, &_PyInstructionSequence_Type);
 | 
						|
    if (seq == NULL) {
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
    seq->s_instrs = NULL;
 | 
						|
    seq->s_allocated = 0;
 | 
						|
    seq->s_used = 0;
 | 
						|
    seq->s_next_free_label = 0;
 | 
						|
    seq->s_labelmap = NULL;
 | 
						|
    seq->s_labelmap_size = 0;
 | 
						|
    seq->s_nested = NULL;
 | 
						|
 | 
						|
    PyObject_GC_Track(seq);
 | 
						|
    return seq;
 | 
						|
}
 | 
						|
 | 
						|
PyObject*
 | 
						|
_PyInstructionSequence_New(void)
 | 
						|
{
 | 
						|
    _PyInstructionSequence *seq = inst_seq_create();
 | 
						|
    if (seq == NULL) {
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
    return (PyObject*)seq;
 | 
						|
}
 | 
						|
 | 
						|
/*[clinic input]
 | 
						|
@classmethod
 | 
						|
InstructionSequenceType.__new__ as inst_seq_new
 | 
						|
 | 
						|
Create a new InstructionSequence object.
 | 
						|
[clinic start generated code]*/
 | 
						|
 | 
						|
static PyObject *
 | 
						|
inst_seq_new_impl(PyTypeObject *type)
 | 
						|
/*[clinic end generated code: output=98881de92c8876f6 input=b393150146849c74]*/
 | 
						|
{
 | 
						|
    return (PyObject*)inst_seq_create();
 | 
						|
}
 | 
						|
 | 
						|
/*[clinic input]
 | 
						|
InstructionSequenceType.use_label
 | 
						|
 | 
						|
  label: int
 | 
						|
 | 
						|
Place label at current location.
 | 
						|
[clinic start generated code]*/
 | 
						|
 | 
						|
static PyObject *
 | 
						|
InstructionSequenceType_use_label_impl(_PyInstructionSequence *self,
 | 
						|
                                       int label)
 | 
						|
/*[clinic end generated code: output=4c06bbacb2854755 input=da55f49bb91841f3]*/
 | 
						|
 | 
						|
{
 | 
						|
    if (_PyInstructionSequence_UseLabel(self, label) < 0) {
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
    Py_RETURN_NONE;
 | 
						|
}
 | 
						|
 | 
						|
/*[clinic input]
 | 
						|
InstructionSequenceType.addop
 | 
						|
 | 
						|
  opcode: int
 | 
						|
  oparg: int
 | 
						|
  lineno: int
 | 
						|
  col_offset: int
 | 
						|
  end_lineno: int
 | 
						|
  end_col_offset: int
 | 
						|
 | 
						|
Append an instruction.
 | 
						|
[clinic start generated code]*/
 | 
						|
 | 
						|
static PyObject *
 | 
						|
InstructionSequenceType_addop_impl(_PyInstructionSequence *self, int opcode,
 | 
						|
                                   int oparg, int lineno, int col_offset,
 | 
						|
                                   int end_lineno, int end_col_offset)
 | 
						|
/*[clinic end generated code: output=af0cc22c048dfbf3 input=012762ac88198713]*/
 | 
						|
{
 | 
						|
    _Py_SourceLocation loc = {lineno, col_offset, end_lineno, end_col_offset};
 | 
						|
    if (_PyInstructionSequence_Addop(self, opcode, oparg, loc) < 0) {
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
    Py_RETURN_NONE;
 | 
						|
}
 | 
						|
 | 
						|
/*[clinic input]
 | 
						|
InstructionSequenceType.new_label -> int
 | 
						|
 | 
						|
Return a new label.
 | 
						|
[clinic start generated code]*/
 | 
						|
 | 
						|
static int
 | 
						|
InstructionSequenceType_new_label_impl(_PyInstructionSequence *self)
 | 
						|
/*[clinic end generated code: output=dcb0589e4f5bf4bd input=c66040b9897bc327]*/
 | 
						|
{
 | 
						|
    _PyJumpTargetLabel lbl = _PyInstructionSequence_NewLabel(self);
 | 
						|
    return lbl.id;
 | 
						|
}
 | 
						|
 | 
						|
/*[clinic input]
 | 
						|
InstructionSequenceType.add_nested
 | 
						|
 | 
						|
  nested: object
 | 
						|
 | 
						|
Add a nested sequence.
 | 
						|
[clinic start generated code]*/
 | 
						|
 | 
						|
static PyObject *
 | 
						|
InstructionSequenceType_add_nested_impl(_PyInstructionSequence *self,
 | 
						|
                                        PyObject *nested)
 | 
						|
/*[clinic end generated code: output=14540fad459f7971 input=f2c482568b3b3c0f]*/
 | 
						|
{
 | 
						|
    if (!_PyInstructionSequence_Check(nested)) {
 | 
						|
        PyErr_Format(PyExc_TypeError,
 | 
						|
                     "expected an instruction sequence, not %T",
 | 
						|
                     Py_TYPE(nested));
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
    if (_PyInstructionSequence_AddNested(self, (_PyInstructionSequence*)nested) < 0) {
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
    Py_RETURN_NONE;
 | 
						|
}
 | 
						|
 | 
						|
/*[clinic input]
 | 
						|
InstructionSequenceType.get_nested
 | 
						|
 | 
						|
Add a nested sequence.
 | 
						|
[clinic start generated code]*/
 | 
						|
 | 
						|
static PyObject *
 | 
						|
InstructionSequenceType_get_nested_impl(_PyInstructionSequence *self)
 | 
						|
/*[clinic end generated code: output=f415112c292630cb input=e429e474c57b95b4]*/
 | 
						|
{
 | 
						|
    if (self->s_nested == NULL) {
 | 
						|
        return PyList_New(0);
 | 
						|
    }
 | 
						|
    return Py_NewRef(self->s_nested);
 | 
						|
}
 | 
						|
 | 
						|
/*[clinic input]
 | 
						|
InstructionSequenceType.get_instructions
 | 
						|
 | 
						|
Return the instructions as a list of tuples or labels.
 | 
						|
[clinic start generated code]*/
 | 
						|
 | 
						|
static PyObject *
 | 
						|
InstructionSequenceType_get_instructions_impl(_PyInstructionSequence *self)
 | 
						|
/*[clinic end generated code: output=23f4f3f894c301b3 input=fbadb5dadb611291]*/
 | 
						|
{
 | 
						|
    if (_PyInstructionSequence_ApplyLabelMap(self) < 0) {
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
    PyObject *instructions = PyList_New(0);
 | 
						|
    if (instructions == NULL) {
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
    for (int i = 0; i < self->s_used; i++) {
 | 
						|
        instruction *instr = &self->s_instrs[i];
 | 
						|
        location loc = instr->i_loc;
 | 
						|
        PyObject *inst_tuple;
 | 
						|
 | 
						|
        if (OPCODE_HAS_ARG(instr->i_opcode)) {
 | 
						|
            inst_tuple = Py_BuildValue(
 | 
						|
                "(iiiiii)", instr->i_opcode, instr->i_oparg,
 | 
						|
                loc.lineno, loc.end_lineno,
 | 
						|
                loc.col_offset, loc.end_col_offset);
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            inst_tuple = Py_BuildValue(
 | 
						|
                "(iOiiii)", instr->i_opcode, Py_None,
 | 
						|
                loc.lineno, loc.end_lineno,
 | 
						|
                loc.col_offset, loc.end_col_offset);
 | 
						|
        }
 | 
						|
        if (inst_tuple == NULL) {
 | 
						|
            goto error;
 | 
						|
        }
 | 
						|
 | 
						|
        int res = PyList_Append(instructions, inst_tuple);
 | 
						|
        Py_DECREF(inst_tuple);
 | 
						|
        if (res != 0) {
 | 
						|
            goto error;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return instructions;
 | 
						|
error:
 | 
						|
    Py_XDECREF(instructions);
 | 
						|
    return NULL;
 | 
						|
}
 | 
						|
 | 
						|
static PyMethodDef inst_seq_methods[] = {
 | 
						|
   INSTRUCTIONSEQUENCETYPE_ADDOP_METHODDEF
 | 
						|
   INSTRUCTIONSEQUENCETYPE_NEW_LABEL_METHODDEF
 | 
						|
   INSTRUCTIONSEQUENCETYPE_USE_LABEL_METHODDEF
 | 
						|
   INSTRUCTIONSEQUENCETYPE_ADD_NESTED_METHODDEF
 | 
						|
   INSTRUCTIONSEQUENCETYPE_GET_NESTED_METHODDEF
 | 
						|
   INSTRUCTIONSEQUENCETYPE_GET_INSTRUCTIONS_METHODDEF
 | 
						|
   {NULL, NULL, 0, NULL},
 | 
						|
};
 | 
						|
 | 
						|
static PyMemberDef inst_seq_memberlist[] = {
 | 
						|
    {NULL}      /* Sentinel */
 | 
						|
};
 | 
						|
 | 
						|
static PyGetSetDef inst_seq_getsetters[] = {
 | 
						|
    {NULL}      /* Sentinel */
 | 
						|
};
 | 
						|
 | 
						|
static void
 | 
						|
inst_seq_dealloc(PyObject *op)
 | 
						|
{
 | 
						|
    _PyInstructionSequence *seq = (_PyInstructionSequence *)op;
 | 
						|
    PyObject_GC_UnTrack(seq);
 | 
						|
    Py_TRASHCAN_BEGIN(seq, inst_seq_dealloc)
 | 
						|
    PyInstructionSequence_Fini(seq);
 | 
						|
    PyObject_GC_Del(seq);
 | 
						|
    Py_TRASHCAN_END
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
inst_seq_traverse(PyObject *op, visitproc visit, void *arg)
 | 
						|
{
 | 
						|
    _PyInstructionSequence *seq = (_PyInstructionSequence *)op;
 | 
						|
    Py_VISIT(seq->s_nested);
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
inst_seq_clear(PyObject *op)
 | 
						|
{
 | 
						|
    _PyInstructionSequence *seq = (_PyInstructionSequence *)op;
 | 
						|
    Py_CLEAR(seq->s_nested);
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
PyTypeObject _PyInstructionSequence_Type = {
 | 
						|
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
 | 
						|
    "InstructionSequence",
 | 
						|
    sizeof(_PyInstructionSequence),
 | 
						|
    0,
 | 
						|
    inst_seq_dealloc,   /*tp_dealloc*/
 | 
						|
    0,                  /*tp_vectorcall_offset*/
 | 
						|
    0,                  /*tp_getattr*/
 | 
						|
    0,                  /*tp_setattr*/
 | 
						|
    0,                  /*tp_as_async*/
 | 
						|
    0,                  /*tp_repr*/
 | 
						|
    0,                  /*tp_as_number*/
 | 
						|
    0,                  /*tp_as_sequence*/
 | 
						|
    0,                  /*tp_as_mapping*/
 | 
						|
    0,                  /* tp_hash */
 | 
						|
    0,                  /* tp_call */
 | 
						|
    0,                  /* tp_str */
 | 
						|
    PyObject_GenericGetAttr,  /* tp_getattro */
 | 
						|
    0,                  /* tp_setattro */
 | 
						|
    0,                  /* tp_as_buffer */
 | 
						|
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
 | 
						|
    inst_seq_new__doc__,                    /* tp_doc */
 | 
						|
    inst_seq_traverse,                      /* tp_traverse */
 | 
						|
    inst_seq_clear,                         /* tp_clear */
 | 
						|
    0,                                      /* tp_richcompare */
 | 
						|
    0,                                      /* tp_weaklistoffset */
 | 
						|
    0,                                      /* tp_iter */
 | 
						|
    0,                                      /* tp_iternext */
 | 
						|
    inst_seq_methods,                       /* tp_methods */
 | 
						|
    inst_seq_memberlist,                    /* tp_members */
 | 
						|
    inst_seq_getsetters,                    /* tp_getset */
 | 
						|
    0,                                      /* tp_base */
 | 
						|
    0,                                      /* tp_dict */
 | 
						|
    0,                                      /* tp_descr_get */
 | 
						|
    0,                                      /* tp_descr_set */
 | 
						|
    0,                                      /* tp_dictoffset */
 | 
						|
    0,                                      /* tp_init */
 | 
						|
    0,                                      /* tp_alloc */
 | 
						|
    inst_seq_new,                           /* tp_new */
 | 
						|
};
 |