mirror of https://github.com/python/cpython.git
454 lines
13 KiB
C
454 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 <stdbool.h>
|
|
|
|
#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"
|
|
|
|
#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 */
|
|
};
|