| /* |
| * 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_compile.h" // _PyCompile_EnsureArrayLargeEnough |
| #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); |
| |
| RETURN_IF_ERROR( |
| _PyCompile_EnsureArrayLargeEnough(seq->s_used + 1, |
| (void**)&seq->s_instrs, |
| &seq->s_allocated, |
| INITIAL_INSTR_SEQUENCE_SIZE, |
| sizeof(instruction))); |
| 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; |
| RETURN_IF_ERROR( |
| _PyCompile_EnsureArrayLargeEnough(lbl, |
| (void**)&seq->s_labelmap, |
| &seq->s_labelmap_size, |
| INITIAL_INSTR_SEQUENCE_LABELS_MAP_SIZE, |
| sizeof(int))); |
| |
| 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(_PyInstructionSequence *seq) |
| { |
| 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(_PyInstructionSequence *seq, visitproc visit, void *arg) |
| { |
| Py_VISIT(seq->s_nested); |
| return 0; |
| } |
| |
| static int |
| inst_seq_clear(_PyInstructionSequence *seq) |
| { |
| Py_CLEAR(seq->s_nested); |
| return 0; |
| } |
| |
| PyTypeObject _PyInstructionSequence_Type = { |
| PyVarObject_HEAD_INIT(&PyType_Type, 0) |
| "InstructionSequence", |
| sizeof(_PyInstructionSequence), |
| 0, |
| (destructor)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 */ |
| (traverseproc)inst_seq_traverse, /* tp_traverse */ |
| (inquiry)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 */ |
| }; |